summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Godot.app/Contents/Resources/Godot.icnsbin294521 -> 260598 bytes
-rw-r--r--tools/SCsub22
-rw-r--r--tools/addheader/addheader.py2
-rw-r--r--tools/collada/SCsub2
-rw-r--r--tools/collada/collada.cpp10
-rw-r--r--tools/collada/collada.h20
-rw-r--r--tools/doc/SCsub2
-rw-r--r--tools/doc/doc_data.cpp23
-rw-r--r--tools/doc/doc_data.h2
-rw-r--r--tools/docdump/SCsub2
-rw-r--r--tools/docdump/class_list.xml2
-rw-r--r--tools/docdump/doc_dump.cpp8
-rw-r--r--tools/docdump/doc_dump.h2
-rw-r--r--tools/docdump/locales/es/LC_MESSAGES/makedocs.mobin0 -> 2321 bytes
-rw-r--r--tools/docdump/locales/es/LC_MESSAGES/makedocs.po142
-rw-r--r--tools/docdump/makedocs.pot108
-rw-r--r--tools/docdump/makedocs.py382
-rw-r--r--tools/docdump/makehtml.py19
-rw-r--r--tools/editor/SCsub11
-rw-r--r--tools/editor/animation_editor.cpp647
-rw-r--r--tools/editor/animation_editor.h49
-rw-r--r--tools/editor/array_property_edit.cpp231
-rw-r--r--tools/editor/array_property_edit.h36
-rw-r--r--tools/editor/call_dialog.cpp2
-rw-r--r--tools/editor/call_dialog.h2
-rw-r--r--tools/editor/code_editor.cpp20
-rw-r--r--tools/editor/code_editor.h2
-rw-r--r--tools/editor/connections_dialog.cpp167
-rw-r--r--tools/editor/connections_dialog.h2
-rw-r--r--tools/editor/console.cpp2
-rw-r--r--tools/editor/console.h2
-rw-r--r--tools/editor/create_dialog.cpp11
-rw-r--r--tools/editor/create_dialog.h2
-rw-r--r--tools/editor/default_saver.cpp2
-rw-r--r--tools/editor/default_saver.h2
-rw-r--r--tools/editor/dependency_editor.cpp512
-rw-r--r--tools/editor/dependency_editor.h94
-rw-r--r--tools/editor/doc_code_font.h10
-rw-r--r--tools/editor/doc_font.h10
-rw-r--r--tools/editor/doc_title_font.h10
-rw-r--r--tools/editor/editor_data.cpp245
-rw-r--r--tools/editor/editor_data.h60
-rw-r--r--tools/editor/editor_dir_dialog.cpp33
-rw-r--r--tools/editor/editor_dir_dialog.h2
-rw-r--r--tools/editor/editor_file_dialog.cpp1367
-rw-r--r--tools/editor/editor_file_dialog.h245
-rw-r--r--tools/editor/editor_file_system.cpp63
-rw-r--r--tools/editor/editor_file_system.h11
-rw-r--r--tools/editor/editor_fonts.cpp96
-rw-r--r--tools/editor/editor_fonts.h18
-rw-r--r--tools/editor/editor_help.cpp598
-rw-r--r--tools/editor/editor_help.h93
-rw-r--r--tools/editor/editor_icons.h2
-rw-r--r--tools/editor/editor_import_export.cpp287
-rw-r--r--tools/editor/editor_import_export.h55
-rw-r--r--tools/editor/editor_log.cpp42
-rw-r--r--tools/editor/editor_log.h14
-rw-r--r--tools/editor/editor_node.cpp2335
-rw-r--r--tools/editor/editor_node.h188
-rw-r--r--tools/editor/editor_path.cpp8
-rw-r--r--tools/editor/editor_path.h2
-rw-r--r--tools/editor/editor_plugin.cpp16
-rw-r--r--tools/editor/editor_plugin.h9
-rw-r--r--tools/editor/editor_reimport_dialog.cpp8
-rw-r--r--tools/editor/editor_reimport_dialog.h2
-rw-r--r--tools/editor/editor_resource_preview.cpp261
-rw-r--r--tools/editor/editor_resource_preview.h95
-rw-r--r--tools/editor/editor_run.cpp99
-rw-r--r--tools/editor/editor_run.h12
-rw-r--r--tools/editor/editor_run_native.cpp53
-rw-r--r--tools/editor/editor_run_native.h15
-rw-r--r--tools/editor/editor_run_script.cpp2
-rw-r--r--tools/editor/editor_selection.cpp2
-rw-r--r--tools/editor/editor_selection.h2
-rw-r--r--tools/editor/editor_settings.cpp152
-rw-r--r--tools/editor/editor_settings.h17
-rw-r--r--tools/editor/editor_sub_scene.cpp6
-rw-r--r--tools/editor/editor_sub_scene.h6
-rw-r--r--tools/editor/editor_vu.cpp2
-rw-r--r--tools/editor/editor_vu.h2
-rw-r--r--tools/editor/file_type_cache.cpp2
-rw-r--r--tools/editor/file_type_cache.h2
-rw-r--r--tools/editor/fileserver/SCsub4
-rw-r--r--tools/editor/fileserver/editor_file_server.cpp22
-rw-r--r--tools/editor/groups_editor.cpp5
-rw-r--r--tools/editor/groups_editor.h2
-rw-r--r--tools/editor/icons/SCsub19
-rw-r--r--tools/editor/icons/icon_add.pngbin393 -> 351 bytes
-rw-r--r--tools/editor/icons/icon_anchor.pngbin0 -> 428 bytes
-rw-r--r--tools/editor/icons/icon_animation.pngbin651 -> 595 bytes
-rw-r--r--tools/editor/icons/icon_arrow_left.pngbin0 -> 243 bytes
-rw-r--r--tools/editor/icons/icon_arrow_left_disabled.pngbin0 -> 244 bytes
-rw-r--r--tools/editor/icons/icon_arrow_right.pngbin0 -> 263 bytes
-rw-r--r--tools/editor/icons/icon_arrow_right_disabled.pngbin0 -> 252 bytes
-rw-r--r--tools/editor/icons/icon_arrow_up.pngbin0 -> 259 bytes
-rw-r--r--tools/editor/icons/icon_arrow_up_disabled.pngbin0 -> 275 bytes
-rw-r--r--tools/editor/icons/icon_atlas_texture.pngbin458 -> 402 bytes
-rw-r--r--tools/editor/icons/icon_audio_stream_gibberish.pngbin456 -> 406 bytes
-rw-r--r--tools/editor/icons/icon_audio_stream_opus.pngbin0 -> 559 bytes
-rw-r--r--tools/editor/icons/icon_auto_play.pngbin217 -> 181 bytes
-rw-r--r--tools/editor/icons/icon_back.pngbin206 -> 180 bytes
-rw-r--r--tools/editor/icons/icon_back_buffer_copy.pngbin0 -> 204 bytes
-rw-r--r--tools/editor/icons/icon_back_disabled.pngbin0 -> 175 bytes
-rw-r--r--tools/editor/icons/icon_bake.pngbin419 -> 374 bytes
-rw-r--r--tools/editor/icons/icon_blend.pngbin488 -> 443 bytes
-rw-r--r--tools/editor/icons/icon_bone.pngbin2992 -> 297 bytes
-rw-r--r--tools/editor/icons/icon_bool.pngbin184 -> 147 bytes
-rw-r--r--tools/editor/icons/icon_canvas_item.pngbin481 -> 434 bytes
-rw-r--r--tools/editor/icons/icon_canvas_item_material.pngbin0 -> 835 bytes
-rw-r--r--tools/editor/icons/icon_canvas_item_shader.pngbin0 -> 568 bytes
-rw-r--r--tools/editor/icons/icon_canvas_item_shader_graph.pngbin0 -> 575 bytes
-rw-r--r--tools/editor/icons/icon_canvas_modulate.pngbin0 -> 441 bytes
-rw-r--r--tools/editor/icons/icon_check_box.pngbin0 -> 344 bytes
-rw-r--r--tools/editor/icons/icon_class_list.pngbin0 -> 241 bytes
-rw-r--r--tools/editor/icons/icon_close.pngbin456 -> 398 bytes
-rw-r--r--tools/editor/icons/icon_close_hover.pngbin444 -> 391 bytes
-rw-r--r--tools/editor/icons/icon_collapse.pngbin343 -> 351 bytes
-rw-r--r--tools/editor/icons/icon_collapse_hl.pngbin341 -> 338 bytes
-rw-r--r--tools/editor/icons/icon_color_ramp.pngbin0 -> 290 bytes
-rw-r--r--tools/editor/icons/icon_connect.pngbin646 -> 592 bytes
-rw-r--r--tools/editor/icons/icon_control_align_bottom_center.pngbin0 -> 174 bytes
-rw-r--r--tools/editor/icons/icon_control_align_bottom_left.pngbin0 -> 172 bytes
-rw-r--r--tools/editor/icons/icon_control_align_bottom_right.pngbin0 -> 174 bytes
-rw-r--r--tools/editor/icons/icon_control_align_bottom_wide.pngbin0 -> 174 bytes
-rw-r--r--tools/editor/icons/icon_control_align_center.pngbin0 -> 181 bytes
-rw-r--r--tools/editor/icons/icon_control_align_center_left.pngbin0 -> 172 bytes
-rw-r--r--tools/editor/icons/icon_control_align_center_right.pngbin0 -> 171 bytes
-rw-r--r--tools/editor/icons/icon_control_align_left_center.pngbin0 -> 183 bytes
-rw-r--r--tools/editor/icons/icon_control_align_left_wide.pngbin0 -> 166 bytes
-rw-r--r--tools/editor/icons/icon_control_align_right_center.pngbin0 -> 181 bytes
-rw-r--r--tools/editor/icons/icon_control_align_right_wide.pngbin0 -> 167 bytes
-rw-r--r--tools/editor/icons/icon_control_align_top_center.pngbin0 -> 181 bytes
-rw-r--r--tools/editor/icons/icon_control_align_top_left.pngbin0 -> 176 bytes
-rw-r--r--tools/editor/icons/icon_control_align_top_right.pngbin0 -> 183 bytes
-rw-r--r--tools/editor/icons/icon_control_align_top_wide.pngbin0 -> 179 bytes
-rw-r--r--tools/editor/icons/icon_control_align_wide.pngbin0 -> 165 bytes
-rw-r--r--tools/editor/icons/icon_control_hcenter_wide.pngbin0 -> 182 bytes
-rw-r--r--tools/editor/icons/icon_control_vcenter_wide.pngbin0 -> 171 bytes
-rw-r--r--tools/editor/icons/icon_curve_constant.pngbin0 -> 222 bytes
-rw-r--r--tools/editor/icons/icon_curve_in.pngbin0 -> 319 bytes
-rw-r--r--tools/editor/icons/icon_curve_in_out.pngbin0 -> 326 bytes
-rw-r--r--tools/editor/icons/icon_curve_linear.pngbin0 -> 275 bytes
-rw-r--r--tools/editor/icons/icon_curve_out.pngbin0 -> 319 bytes
-rw-r--r--tools/editor/icons/icon_curve_out_in.pngbin0 -> 318 bytes
-rw-r--r--tools/editor/icons/icon_debug.pngbin0 -> 659 bytes
-rw-r--r--tools/editor/icons/icon_default_project_icon.pngbin3621 -> 2713 bytes
-rw-r--r--tools/editor/icons/icon_del.pngbin456 -> 398 bytes
-rw-r--r--tools/editor/icons/icon_duplicate.pngbin363 -> 327 bytes
-rw-r--r--tools/editor/icons/icon_edit.pngbin523 -> 475 bytes
-rw-r--r--tools/editor/icons/icon_edit_key.pngbin534 -> 478 bytes
-rw-r--r--tools/editor/icons/icon_edit_resource.pngbin286 -> 234 bytes
-rw-r--r--tools/editor/icons/icon_editor_focus.pngbin1273 -> 629 bytes
-rw-r--r--tools/editor/icons/icon_enum.pngbin221 -> 193 bytes
-rw-r--r--tools/editor/icons/icon_event_player.pngbin352 -> 299 bytes
-rw-r--r--tools/editor/icons/icon_favorites.pngbin375 -> 412 bytes
-rw-r--r--tools/editor/icons/icon_file_big.pngbin0 -> 986 bytes
-rw-r--r--tools/editor/icons/icon_file_list.pngbin0 -> 288 bytes
-rw-r--r--tools/editor/icons/icon_file_server.pngbin636 -> 563 bytes
-rw-r--r--tools/editor/icons/icon_file_thumbnail.pngbin0 -> 296 bytes
-rw-r--r--tools/editor/icons/icon_filesystem.pngbin0 -> 241 bytes
-rw-r--r--tools/editor/icons/icon_folder.pngbin471 -> 428 bytes
-rw-r--r--tools/editor/icons/icon_folder_big.pngbin0 -> 1215 bytes
-rw-r--r--tools/editor/icons/icon_font.pngbin282 -> 248 bytes
-rw-r--r--tools/editor/icons/icon_forward.pngbin199 -> 191 bytes
-rw-r--r--tools/editor/icons/icon_g_d_script.pngbin628 -> 580 bytes
-rw-r--r--tools/editor/icons/icon_godot.pngbin0 -> 671 bytes
-rw-r--r--tools/editor/icons/icon_graph_color_ramp.pngbin0 -> 290 bytes
-rw-r--r--tools/editor/icons/icon_graph_comment.pngbin0 -> 310 bytes
-rw-r--r--tools/editor/icons/icon_graph_cube_uniform.pngbin0 -> 652 bytes
-rw-r--r--tools/editor/icons/icon_graph_curve_map.pngbin0 -> 619 bytes
-rw-r--r--tools/editor/icons/icon_graph_default_texture.pngbin0 -> 326 bytes
-rw-r--r--tools/editor/icons/icon_graph_input.pngbin0 -> 585 bytes
-rw-r--r--tools/editor/icons/icon_graph_rgb.pngbin0 -> 174 bytes
-rw-r--r--tools/editor/icons/icon_graph_rgb_op.pngbin0 -> 280 bytes
-rw-r--r--tools/editor/icons/icon_graph_rgb_uniform.pngbin0 -> 419 bytes
-rw-r--r--tools/editor/icons/icon_graph_scalar.pngbin0 -> 328 bytes
-rw-r--r--tools/editor/icons/icon_graph_scalar_interp.pngbin0 -> 352 bytes
-rw-r--r--tools/editor/icons/icon_graph_scalar_op.pngbin0 -> 238 bytes
-rw-r--r--tools/editor/icons/icon_graph_scalar_uniform.pngbin0 -> 392 bytes
-rw-r--r--tools/editor/icons/icon_graph_scalars_to_vec.pngbin0 -> 300 bytes
-rw-r--r--tools/editor/icons/icon_graph_texscreen.pngbin0 -> 578 bytes
-rw-r--r--tools/editor/icons/icon_graph_texture_uniform.pngbin0 -> 358 bytes
-rw-r--r--tools/editor/icons/icon_graph_time.pngbin0 -> 678 bytes
-rw-r--r--tools/editor/icons/icon_graph_vec_dp.pngbin0 -> 279 bytes
-rw-r--r--tools/editor/icons/icon_graph_vec_interp.pngbin0 -> 332 bytes
-rw-r--r--tools/editor/icons/icon_graph_vec_length.pngbin0 -> 303 bytes
-rw-r--r--tools/editor/icons/icon_graph_vec_op.pngbin0 -> 256 bytes
-rw-r--r--tools/editor/icons/icon_graph_vec_scalar_op.pngbin0 -> 267 bytes
-rw-r--r--tools/editor/icons/icon_graph_vec_to_scalars.pngbin0 -> 306 bytes
-rw-r--r--tools/editor/icons/icon_graph_vecs_to_xform.pngbin0 -> 288 bytes
-rw-r--r--tools/editor/icons/icon_graph_vector.pngbin0 -> 480 bytes
-rw-r--r--tools/editor/icons/icon_graph_vector_uniform.pngbin0 -> 531 bytes
-rw-r--r--tools/editor/icons/icon_graph_xform.pngbin0 -> 262 bytes
-rw-r--r--tools/editor/icons/icon_graph_xform_mult.pngbin0 -> 311 bytes
-rw-r--r--tools/editor/icons/icon_graph_xform_scalar_func.pngbin0 -> 378 bytes
-rw-r--r--tools/editor/icons/icon_graph_xform_to_vecs.pngbin0 -> 272 bytes
-rw-r--r--tools/editor/icons/icon_graph_xform_uniform.pngbin0 -> 346 bytes
-rw-r--r--tools/editor/icons/icon_graph_xform_vec_func.pngbin0 -> 389 bytes
-rw-r--r--tools/editor/icons/icon_graph_xform_vec_imult.pngbin0 -> 430 bytes
-rw-r--r--tools/editor/icons/icon_graph_xform_vec_mult.pngbin0 -> 422 bytes
-rw-r--r--tools/editor/icons/icon_grid.pngbin0 -> 354 bytes
-rw-r--r--tools/editor/icons/icon_group.pngbin402 -> 357 bytes
-rw-r--r--tools/editor/icons/icon_groups.pngbin558 -> 507 bytes
-rw-r--r--tools/editor/icons/icon_help.pngbin822 -> 841 bytes
-rw-r--r--tools/editor/icons/icon_hidden.pngbin553 -> 500 bytes
-rw-r--r--tools/editor/icons/icon_history.pngbin0 -> 572 bytes
-rw-r--r--tools/editor/icons/icon_image.pngbin444 -> 403 bytes
-rw-r--r--tools/editor/icons/icon_image_texture.pngbin352 -> 315 bytes
-rw-r--r--tools/editor/icons/icon_instance_options.pngbin523 -> 483 bytes
-rw-r--r--tools/editor/icons/icon_integer.pngbin183 -> 142 bytes
-rw-r--r--tools/editor/icons/icon_interp_cubic.pngbin373 -> 340 bytes
-rw-r--r--tools/editor/icons/icon_interp_linear.pngbin172 -> 134 bytes
-rw-r--r--tools/editor/icons/icon_interp_raw.pngbin161 -> 127 bytes
-rw-r--r--tools/editor/icons/icon_item_list.pngbin0 -> 341 bytes
-rw-r--r--tools/editor/icons/icon_key.pngbin495 -> 450 bytes
-rw-r--r--tools/editor/icons/icon_key_next.pngbin0 -> 499 bytes
-rw-r--r--tools/editor/icons/icon_key_selected.pngbin257 -> 215 bytes
-rw-r--r--tools/editor/icons/icon_light_2d.pngbin0 -> 342 bytes
-rw-r--r--tools/editor/icons/icon_light_map.pngbin441 -> 406 bytes
-rw-r--r--tools/editor/icons/icon_light_occluder_2d.pngbin0 -> 516 bytes
-rw-r--r--tools/editor/icons/icon_live_debug.pngbin0 -> 583 bytes
-rw-r--r--tools/editor/icons/icon_load.pngbin471 -> 428 bytes
-rw-r--r--tools/editor/icons/icon_lock.pngbin403 -> 362 bytes
-rw-r--r--tools/editor/icons/icon_loop.pngbin408 -> 366 bytes
-rw-r--r--tools/editor/icons/icon_main_play.pngbin367 -> 311 bytes
-rw-r--r--tools/editor/icons/icon_main_stop.pngbin336 -> 263 bytes
-rw-r--r--tools/editor/icons/icon_material_shader.pngbin0 -> 844 bytes
-rw-r--r--tools/editor/icons/icon_material_shader_graph.pngbin0 -> 949 bytes
-rw-r--r--tools/editor/icons/icon_mirror_x.pngbin230 -> 187 bytes
-rw-r--r--tools/editor/icons/icon_mirror_y.pngbin244 -> 215 bytes
-rw-r--r--tools/editor/icons/icon_move_down.pngbin286 -> 239 bytes
-rw-r--r--tools/editor/icons/icon_move_down_hl.pngbin266 -> 234 bytes
-rw-r--r--tools/editor/icons/icon_move_up.pngbin275 -> 225 bytes
-rw-r--r--tools/editor/icons/icon_move_up_hl.pngbin278 -> 224 bytes
-rw-r--r--tools/editor/icons/icon_multi_edit.pngbin0 -> 576 bytes
-rw-r--r--tools/editor/icons/icon_multi_node_edit.pngbin0 -> 576 bytes
-rw-r--r--tools/editor/icons/icon_navigation_2d.pngbin0 -> 541 bytes
-rw-r--r--tools/editor/icons/icon_navigation_polygon_instance.pngbin0 -> 391 bytes
-rw-r--r--tools/editor/icons/icon_new.pngbin344 -> 298 bytes
-rw-r--r--tools/editor/icons/icon_node.pngbin301 -> 257 bytes
-rw-r--r--tools/editor/icons/icon_non_favorite.pngbin0 -> 469 bytes
-rw-r--r--tools/editor/icons/icon_occluder_polygon_2d.pngbin0 -> 581 bytes
-rw-r--r--tools/editor/icons/icon_open.pngbin471 -> 428 bytes
-rw-r--r--tools/editor/icons/icon_p_hash_translation.pngbin282 -> 234 bytes
-rw-r--r--tools/editor/icons/icon_packed_scene.pngbin536 -> 485 bytes
-rw-r--r--tools/editor/icons/icon_panel_top.pngbin0 -> 195 bytes
-rw-r--r--tools/editor/icons/icon_panels_1.pngbin195 -> 154 bytes
-rw-r--r--tools/editor/icons/icon_panels_2.pngbin196 -> 157 bytes
-rw-r--r--tools/editor/icons/icon_panels_3.pngbin205 -> 166 bytes
-rw-r--r--tools/editor/icons/icon_panels_4.pngbin198 -> 159 bytes
-rw-r--r--tools/editor/icons/icon_patch_9_frame.pngbin0 -> 431 bytes
-rw-r--r--tools/editor/icons/icon_pause.pngbin367 -> 228 bytes
-rw-r--r--tools/editor/icons/icon_pin.pngbin420 -> 381 bytes
-rw-r--r--tools/editor/icons/icon_pin_pressed.pngbin385 -> 346 bytes
-rw-r--r--tools/editor/icons/icon_play.pngbin302 -> 260 bytes
-rw-r--r--tools/editor/icons/icon_play_backwards.pngbin0 -> 256 bytes
-rw-r--r--tools/editor/icons/icon_play_custom.pngbin533 -> 500 bytes
-rw-r--r--tools/editor/icons/icon_play_scene.pngbin690 -> 600 bytes
-rw-r--r--tools/editor/icons/icon_play_start.pngbin0 -> 282 bytes
-rw-r--r--tools/editor/icons/icon_play_start_backwards.pngbin0 -> 289 bytes
-rw-r--r--tools/editor/icons/icon_prev_scene.pngbin279 -> 229 bytes
-rw-r--r--tools/editor/icons/icon_real.pngbin183 -> 146 bytes
-rw-r--r--tools/editor/icons/icon_region_edit.pngbin0 -> 171 bytes
-rw-r--r--tools/editor/icons/icon_reload.pngbin567 -> 519 bytes
-rw-r--r--tools/editor/icons/icon_remote.pngbin0 -> 707 bytes
-rw-r--r--tools/editor/icons/icon_remove.pngbin456 -> 519 bytes
-rw-r--r--tools/editor/icons/icon_remove_small.pngbin0 -> 297 bytes
-rw-r--r--tools/editor/icons/icon_rename.pngbin320 -> 269 bytes
-rw-r--r--tools/editor/icons/icon_reparent.pngbin523 -> 474 bytes
-rw-r--r--tools/editor/icons/icon_replace.pngbin483 -> 435 bytes
-rw-r--r--tools/editor/icons/icon_resource_preloader.pngbin771 -> 716 bytes
-rw-r--r--tools/editor/icons/icon_rotate_0.pngbin0 -> 207 bytes
-rw-r--r--tools/editor/icons/icon_rotate_180.pngbin0 -> 303 bytes
-rw-r--r--tools/editor/icons/icon_rotate_270.pngbin0 -> 343 bytes
-rw-r--r--tools/editor/icons/icon_rotate_90.pngbin0 -> 256 bytes
-rw-r--r--tools/editor/icons/icon_sample_player.pngbin448 -> 415 bytes
-rw-r--r--tools/editor/icons/icon_save.pngbin515 -> 473 bytes
-rw-r--r--tools/editor/icons/icon_script.pngbin317 -> 272 bytes
-rw-r--r--tools/editor/icons/icon_script_list.pngbin0 -> 213 bytes
-rw-r--r--tools/editor/icons/icon_sound_room_params.pngbin577 -> 534 bytes
-rw-r--r--tools/editor/icons/icon_stop.pngbin171 -> 236 bytes
-rw-r--r--tools/editor/icons/icon_stream_player.pngbin355 -> 327 bytes
-rw-r--r--tools/editor/icons/icon_string.pngbin156 -> 121 bytes
-rw-r--r--tools/editor/icons/icon_tab_menu.pngbin0 -> 251 bytes
-rw-r--r--tools/editor/icons/icon_texture.pngbin328 -> 292 bytes
-rw-r--r--tools/editor/icons/icon_timer.pngbin607 -> 551 bytes
-rw-r--r--tools/editor/icons/icon_tool_move.pngbin323 -> 275 bytes
-rw-r--r--tools/editor/icons/icon_tool_pan.pngbin1555 -> 1654 bytes
-rw-r--r--tools/editor/icons/icon_tool_rotate.pngbin610 -> 567 bytes
-rw-r--r--tools/editor/icons/icon_tool_scale.pngbin415 -> 366 bytes
-rw-r--r--tools/editor/icons/icon_tool_select.pngbin359 -> 311 bytes
-rw-r--r--tools/editor/icons/icon_tools.pngbin639 -> 589 bytes
-rw-r--r--tools/editor/icons/icon_track_continuous.pngbin268 -> 231 bytes
-rw-r--r--tools/editor/icons/icon_track_discrete.pngbin153 -> 118 bytes
-rw-r--r--tools/editor/icons/icon_translation.pngbin286 -> 241 bytes
-rw-r--r--tools/editor/icons/icon_transpose.pngbin0 -> 244 bytes
-rw-r--r--tools/editor/icons/icon_unbone.pngbin3000 -> 303 bytes
-rw-r--r--tools/editor/icons/icon_ungroup.pngbin525 -> 478 bytes
-rw-r--r--tools/editor/icons/icon_unlock.pngbin367 -> 337 bytes
-rw-r--r--tools/editor/icons/icon_uv.pngbin381 -> 343 bytes
-rw-r--r--tools/editor/icons/icon_vector.pngbin190 -> 158 bytes
-rw-r--r--tools/editor/icons/icon_vector2.pngbin176 -> 141 bytes
-rw-r--r--tools/editor/icons/icon_viewport.pngbin325 -> 287 bytes
-rw-r--r--tools/editor/icons/icon_visible.pngbin563 -> 502 bytes
-rw-r--r--tools/editor/icons/icon_wait_no_preview.pngbin0 -> 1041 bytes
-rw-r--r--tools/editor/icons/icon_wait_preview_1.pngbin0 -> 1208 bytes
-rw-r--r--tools/editor/icons/icon_wait_preview_2.pngbin0 -> 1270 bytes
-rw-r--r--tools/editor/icons/icon_wait_preview_3.pngbin0 -> 1190 bytes
-rw-r--r--tools/editor/icons/icon_wait_preview_4.pngbin0 -> 1269 bytes
-rw-r--r--tools/editor/icons/icon_wait_preview_5.pngbin0 -> 1191 bytes
-rw-r--r--tools/editor/icons/icon_wait_preview_6.pngbin0 -> 1278 bytes
-rw-r--r--tools/editor/icons/icon_wait_preview_7.pngbin0 -> 1192 bytes
-rw-r--r--tools/editor/icons/icon_wait_preview_8.pngbin0 -> 1341 bytes
-rw-r--r--tools/editor/icons/icon_zoom.pngbin582 -> 537 bytes
-rw-r--r--tools/editor/import_settings.cpp2
-rw-r--r--tools/editor/import_settings.h2
-rw-r--r--tools/editor/inspector_dock.cpp22
-rw-r--r--tools/editor/inspector_dock.h35
-rw-r--r--tools/editor/io_plugins/SCsub4
-rw-r--r--tools/editor/io_plugins/editor_atlas.cpp14
-rw-r--r--tools/editor/io_plugins/editor_atlas.h2
-rw-r--r--tools/editor/io_plugins/editor_font_import_plugin.cpp373
-rw-r--r--tools/editor/io_plugins/editor_font_import_plugin.h2
-rw-r--r--tools/editor/io_plugins/editor_import_collada.cpp393
-rw-r--r--tools/editor/io_plugins/editor_import_collada.h2
-rw-r--r--tools/editor/io_plugins/editor_mesh_import_plugin.cpp14
-rw-r--r--tools/editor/io_plugins/editor_sample_import_plugin.cpp134
-rw-r--r--tools/editor/io_plugins/editor_sample_import_plugin.h16
-rw-r--r--tools/editor/io_plugins/editor_scene_import_plugin.cpp99
-rw-r--r--tools/editor/io_plugins/editor_scene_import_plugin.h4
-rw-r--r--tools/editor/io_plugins/editor_texture_import_plugin.cpp507
-rw-r--r--tools/editor/io_plugins/editor_texture_import_plugin.h12
-rw-r--r--tools/editor/io_plugins/editor_translation_import_plugin.cpp12
-rw-r--r--tools/editor/io_plugins/editor_translation_import_plugin.h2
-rw-r--r--tools/editor/multi_node_edit.cpp118
-rw-r--r--tools/editor/multi_node_edit.h32
-rw-r--r--tools/editor/optimized_save_dialog.cpp2
-rw-r--r--tools/editor/optimized_save_dialog.h2
-rw-r--r--tools/editor/output_strings.cpp2
-rw-r--r--tools/editor/output_strings.h124
-rw-r--r--tools/editor/pane_drag.cpp2
-rw-r--r--tools/editor/pane_drag.h48
-rw-r--r--tools/editor/plugins/SCsub4
-rw-r--r--tools/editor/plugins/animation_data_editor_plugin.cpp12
-rw-r--r--tools/editor/plugins/animation_data_editor_plugin.h22
-rw-r--r--tools/editor/plugins/animation_player_editor_plugin.cpp490
-rw-r--r--tools/editor/plugins/animation_player_editor_plugin.h48
-rw-r--r--tools/editor/plugins/animation_tree_editor_plugin.cpp6
-rw-r--r--tools/editor/plugins/animation_tree_editor_plugin.h4
-rw-r--r--tools/editor/plugins/baked_light_baker.cpp3
-rw-r--r--tools/editor/plugins/baked_light_editor_plugin.cpp4
-rw-r--r--tools/editor/plugins/camera_editor_plugin.cpp2
-rw-r--r--tools/editor/plugins/camera_editor_plugin.h2
-rw-r--r--tools/editor/plugins/canvas_item_editor_plugin.cpp959
-rw-r--r--tools/editor/plugins/canvas_item_editor_plugin.h77
-rw-r--r--tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp20
-rw-r--r--tools/editor/plugins/collision_polygon_2d_editor_plugin.h1
-rw-r--r--tools/editor/plugins/collision_polygon_editor_plugin.cpp27
-rw-r--r--tools/editor/plugins/collision_polygon_editor_plugin.h3
-rw-r--r--tools/editor/plugins/collision_shape_2d_editor_plugin.cpp573
-rw-r--r--tools/editor/plugins/collision_shape_2d_editor_plugin.h73
-rw-r--r--tools/editor/plugins/color_ramp_editor_plugin.cpp83
-rw-r--r--tools/editor/plugins/color_ramp_editor_plugin.h37
-rw-r--r--tools/editor/plugins/control_editor_plugin.cpp2
-rw-r--r--tools/editor/plugins/control_editor_plugin.h2
-rw-r--r--tools/editor/plugins/cube_grid_theme_editor_plugin.cpp6
-rw-r--r--tools/editor/plugins/cube_grid_theme_editor_plugin.h4
-rw-r--r--tools/editor/plugins/editor_preview_plugins.cpp783
-rw-r--r--tools/editor/plugins/editor_preview_plugins.h88
-rw-r--r--tools/editor/plugins/item_list_editor_plugin.cpp3
-rw-r--r--tools/editor/plugins/item_list_editor_plugin.h2
-rw-r--r--tools/editor/plugins/light_occluder_2d_editor_plugin.cpp492
-rw-r--r--tools/editor/plugins/light_occluder_2d_editor_plugin.h87
-rw-r--r--tools/editor/plugins/mesh_editor_plugin.cpp30
-rw-r--r--tools/editor/plugins/multimesh_editor_plugin.cpp59
-rw-r--r--tools/editor/plugins/multimesh_editor_plugin.h10
-rw-r--r--tools/editor/plugins/navigation_polygon_editor_plugin.cpp540
-rw-r--r--tools/editor/plugins/navigation_polygon_editor_plugin.h90
-rw-r--r--tools/editor/plugins/particles_2d_editor_plugin.cpp7
-rw-r--r--tools/editor/plugins/particles_2d_editor_plugin.h110
-rw-r--r--tools/editor/plugins/particles_editor_plugin.cpp15
-rw-r--r--tools/editor/plugins/particles_editor_plugin.h4
-rw-r--r--tools/editor/plugins/path_2d_editor_plugin.cpp37
-rw-r--r--tools/editor/plugins/path_2d_editor_plugin.h3
-rw-r--r--tools/editor/plugins/path_editor_plugin.cpp1194
-rw-r--r--tools/editor/plugins/path_editor_plugin.h2
-rw-r--r--tools/editor/plugins/polygon_2d_editor_plugin.cpp187
-rw-r--r--tools/editor/plugins/polygon_2d_editor_plugin.h18
-rw-r--r--tools/editor/plugins/resource_preloader_editor_plugin.cpp10
-rw-r--r--tools/editor/plugins/resource_preloader_editor_plugin.h4
-rw-r--r--tools/editor/plugins/rich_text_editor_plugin.cpp20
-rw-r--r--tools/editor/plugins/rich_text_editor_plugin.h8
-rw-r--r--tools/editor/plugins/sample_editor_plugin.cpp127
-rw-r--r--tools/editor/plugins/sample_editor_plugin.h2
-rw-r--r--tools/editor/plugins/sample_library_editor_plugin.cpp16
-rw-r--r--tools/editor/plugins/sample_library_editor_plugin.h4
-rw-r--r--tools/editor/plugins/sample_player_editor_plugin.cpp3
-rw-r--r--tools/editor/plugins/sample_player_editor_plugin.h2
-rw-r--r--tools/editor/plugins/script_editor_plugin.cpp1844
-rw-r--r--tools/editor/plugins/script_editor_plugin.h120
-rw-r--r--tools/editor/plugins/shader_editor_plugin.cpp75
-rw-r--r--tools/editor/plugins/shader_editor_plugin.h8
-rw-r--r--tools/editor/plugins/shader_graph_editor_plugin.cpp3203
-rw-r--r--tools/editor/plugins/shader_graph_editor_plugin.h247
-rw-r--r--tools/editor/plugins/spatial_editor_plugin.cpp391
-rw-r--r--tools/editor/plugins/spatial_editor_plugin.h35
-rw-r--r--tools/editor/plugins/sprite_frames_editor_plugin.cpp47
-rw-r--r--tools/editor/plugins/sprite_frames_editor_plugin.h6
-rw-r--r--tools/editor/plugins/sprite_region_editor_plugin.cpp565
-rw-r--r--tools/editor/plugins/sprite_region_editor_plugin.h125
-rw-r--r--tools/editor/plugins/stream_editor_plugin.cpp2
-rw-r--r--tools/editor/plugins/stream_editor_plugin.h2
-rw-r--r--tools/editor/plugins/style_box_editor_plugin.cpp2
-rw-r--r--tools/editor/plugins/style_box_editor_plugin.h2
-rw-r--r--tools/editor/plugins/theme_editor_plugin.cpp26
-rw-r--r--tools/editor/plugins/theme_editor_plugin.h6
-rw-r--r--tools/editor/plugins/tile_map_editor_plugin.cpp420
-rw-r--r--tools/editor/plugins/tile_map_editor_plugin.h44
-rw-r--r--tools/editor/plugins/tile_set_editor_plugin.cpp176
-rw-r--r--tools/editor/plugins/tile_set_editor_plugin.h4
-rw-r--r--tools/editor/progress_dialog.cpp8
-rw-r--r--tools/editor/progress_dialog.h2
-rw-r--r--tools/editor/project_export.cpp81
-rw-r--r--tools/editor/project_export.h19
-rw-r--r--tools/editor/project_manager.cpp65
-rw-r--r--tools/editor/project_manager.h2
-rw-r--r--tools/editor/project_settings.cpp235
-rw-r--r--tools/editor/project_settings.h19
-rw-r--r--tools/editor/property_editor.cpp1185
-rw-r--r--tools/editor/property_editor.h72
-rw-r--r--tools/editor/pvrtc_compress.cpp30
-rw-r--r--tools/editor/pvrtc_compress.h2
-rw-r--r--tools/editor/quick_open.cpp44
-rw-r--r--tools/editor/quick_open.h12
-rw-r--r--tools/editor/register_exporters.h2
-rw-r--r--tools/editor/reparent_dialog.cpp8
-rw-r--r--tools/editor/reparent_dialog.h2
-rw-r--r--tools/editor/resources_dock.cpp8
-rw-r--r--tools/editor/resources_dock.h5
-rw-r--r--tools/editor/run_settings_dialog.cpp2
-rw-r--r--tools/editor/run_settings_dialog.h2
-rw-r--r--tools/editor/scene_tree_dock.cpp242
-rw-r--r--tools/editor/scene_tree_dock.h9
-rw-r--r--tools/editor/scene_tree_editor.cpp153
-rw-r--r--tools/editor/scene_tree_editor.h15
-rw-r--r--tools/editor/scenes.cpp2
-rw-r--r--tools/editor/scenes.h2
-rw-r--r--tools/editor/scenes_dock.cpp1190
-rw-r--r--tools/editor/scenes_dock.h132
-rw-r--r--tools/editor/script_create_dialog.cpp22
-rw-r--r--tools/editor/script_create_dialog.h6
-rw-r--r--tools/editor/script_editor_debugger.cpp601
-rw-r--r--tools/editor/script_editor_debugger.h272
-rw-r--r--tools/editor/settings_config_dialog.cpp47
-rw-r--r--tools/editor/settings_config_dialog.h6
-rw-r--r--tools/editor/spatial_editor_gizmos.cpp6381
-rw-r--r--tools/editor/spatial_editor_gizmos.h978
-rw-r--r--tools/export/blender25/godot_export_manager.py472
-rw-r--r--tools/export/blender25/io_scene_dae/__init__.py7
-rw-r--r--tools/export/blender25/io_scene_dae/export_dae.py203
-rw-r--r--tools/freetype/SCsub4
-rw-r--r--tools/html_fs/godot.html1317
-rw-r--r--tools/html_fs/godotfs.js (renamed from tools/html_fs/filesystem.js)0
-rw-r--r--tools/osx_template.app/Contents/Resources/icon.icnsbin294521 -> 260598 bytes
-rw-r--r--tools/pck/SCsub1
-rw-r--r--tools/pck/pck_packer.cpp2
-rw-r--r--tools/pck/pck_packer.h6
-rw-r--r--tools/pe_bliss/README84
-rw-r--r--tools/pe_bliss/SCsub5
-rw-r--r--tools/pe_bliss/entropy.cpp111
-rw-r--r--tools/pe_bliss/entropy.h51
-rw-r--r--tools/pe_bliss/file_version_info.cpp440
-rw-r--r--tools/pe_bliss/file_version_info.h199
-rw-r--r--tools/pe_bliss/message_table.cpp81
-rw-r--r--tools/pe_bliss/message_table.h56
-rw-r--r--tools/pe_bliss/pe_base.cpp1680
-rw-r--r--tools/pe_bliss/pe_base.h544
-rw-r--r--tools/pe_bliss/pe_bliss.h39
-rw-r--r--tools/pe_bliss/pe_bliss_godot.cpp118
-rw-r--r--tools/pe_bliss/pe_bliss_godot.h7
-rw-r--r--tools/pe_bliss/pe_bliss_resources.h36
-rw-r--r--tools/pe_bliss/pe_bound_import.cpp311
-rw-r--r--tools/pe_bliss/pe_bound_import.h108
-rw-r--r--tools/pe_bliss/pe_checksum.cpp103
-rw-r--r--tools/pe_bliss/pe_checksum.h30
-rw-r--r--tools/pe_bliss/pe_debug.cpp865
-rw-r--r--tools/pe_bliss/pe_debug.h324
-rw-r--r--tools/pe_bliss/pe_directory.cpp59
-rw-r--r--tools/pe_bliss/pe_directory.h50
-rw-r--r--tools/pe_bliss/pe_dotnet.cpp186
-rw-r--r--tools/pe_bliss/pe_dotnet.h97
-rw-r--r--tools/pe_bliss/pe_exception.cpp40
-rw-r--r--tools/pe_bliss/pe_exception.h130
-rw-r--r--tools/pe_bliss/pe_exception_directory.cpp177
-rw-r--r--tools/pe_bliss/pe_exception_directory.h88
-rw-r--r--tools/pe_bliss/pe_exports.cpp700
-rw-r--r--tools/pe_bliss/pe_exports.h184
-rw-r--r--tools/pe_bliss/pe_factory.cpp43
-rw-r--r--tools/pe_bliss/pe_factory.h39
-rw-r--r--tools/pe_bliss/pe_imports.cpp777
-rw-r--r--tools/pe_bliss/pe_imports.h208
-rw-r--r--tools/pe_bliss/pe_load_config.cpp557
-rw-r--r--tools/pe_bliss/pe_load_config.h184
-rw-r--r--tools/pe_bliss/pe_properties.cpp41
-rw-r--r--tools/pe_bliss/pe_properties.h236
-rw-r--r--tools/pe_bliss/pe_properties_generic.cpp645
-rw-r--r--tools/pe_bliss/pe_properties_generic.h277
-rw-r--r--tools/pe_bliss/pe_rebuilder.cpp214
-rw-r--r--tools/pe_bliss/pe_rebuilder.h40
-rw-r--r--tools/pe_bliss/pe_relocations.cpp320
-rw-r--r--tools/pe_bliss/pe_relocations.h122
-rw-r--r--tools/pe_bliss/pe_resource_manager.cpp286
-rw-r--r--tools/pe_bliss/pe_resource_manager.h113
-rw-r--r--tools/pe_bliss/pe_resource_viewer.cpp382
-rw-r--r--tools/pe_bliss/pe_resource_viewer.h153
-rw-r--r--tools/pe_bliss/pe_resources.cpp726
-rw-r--r--tools/pe_bliss/pe_resources.h245
-rw-r--r--tools/pe_bliss/pe_rich_data.cpp152
-rw-r--r--tools/pe_bliss/pe_rich_data.h58
-rw-r--r--tools/pe_bliss/pe_section.cpp303
-rw-r--r--tools/pe_bliss/pe_section.h158
-rw-r--r--tools/pe_bliss/pe_structures.h1028
-rw-r--r--tools/pe_bliss/pe_tls.cpp396
-rw-r--r--tools/pe_bliss/pe_tls.h122
-rw-r--r--tools/pe_bliss/resource_bitmap_reader.cpp86
-rw-r--r--tools/pe_bliss/resource_bitmap_reader.h50
-rw-r--r--tools/pe_bliss/resource_bitmap_writer.cpp75
-rw-r--r--tools/pe_bliss/resource_bitmap_writer.h47
-rw-r--r--tools/pe_bliss/resource_cursor_icon_reader.cpp521
-rw-r--r--tools/pe_bliss/resource_cursor_icon_reader.h84
-rw-r--r--tools/pe_bliss/resource_cursor_icon_writer.cpp447
-rw-r--r--tools/pe_bliss/resource_cursor_icon_writer.h94
-rw-r--r--tools/pe_bliss/resource_data_info.cpp48
-rw-r--r--tools/pe_bliss/resource_data_info.h48
-rw-r--r--tools/pe_bliss/resource_internal.h34
-rw-r--r--tools/pe_bliss/resource_message_list_reader.cpp131
-rw-r--r--tools/pe_bliss/resource_message_list_reader.h49
-rw-r--r--tools/pe_bliss/resource_string_table_reader.cpp109
-rw-r--r--tools/pe_bliss/resource_string_table_reader.h57
-rw-r--r--tools/pe_bliss/resource_version_info_reader.cpp311
-rw-r--r--tools/pe_bliss/resource_version_info_reader.h67
-rw-r--r--tools/pe_bliss/resource_version_info_writer.cpp283
-rw-r--r--tools/pe_bliss/resource_version_info_writer.h52
-rw-r--r--tools/pe_bliss/stdint_defs.h45
-rw-r--r--tools/pe_bliss/utils.cpp85
-rw-r--r--tools/pe_bliss/utils.h105
-rw-r--r--tools/pe_bliss/version_info_editor.cpp184
-rw-r--r--tools/pe_bliss/version_info_editor.h79
-rw-r--r--tools/pe_bliss/version_info_types.h38
-rw-r--r--tools/pe_bliss/version_info_viewer.cpp180
-rw-r--r--tools/pe_bliss/version_info_viewer.h89
-rw-r--r--tools/script_plugins/time/time.gd4
-rwxr-xr-xtools/scripts/file-hex-array.py52
-rw-r--r--tools/scripts/makeargs.py (renamed from tools/scrits/makeargs.py)0
554 files changed, 47730 insertions, 9234 deletions
diff --git a/tools/Godot.app/Contents/Resources/Godot.icns b/tools/Godot.app/Contents/Resources/Godot.icns
index 18bc68d6ea..4a3dc0415a 100644
--- a/tools/Godot.app/Contents/Resources/Godot.icns
+++ b/tools/Godot.app/Contents/Resources/Godot.icns
Binary files differ
diff --git a/tools/SCsub b/tools/SCsub
index 875663b849..f046e9ad08 100644
--- a/tools/SCsub
+++ b/tools/SCsub
@@ -5,16 +5,16 @@ env.add_source_files(env.tool_sources,"*.cpp")
Export('env')
-SConscript('editor/SCsub');
-#SConscript('scintilla/SCsub');
-SConscript('collada/SCsub');
-SConscript('docdump/SCsub');
-SConscript('freetype/SCsub');
-SConscript('doc/SCsub');
-SConscript('pck/SCsub');
-
-lib = env.Library("tool",env.tool_sources)
-
-env.Prepend(LIBS=[lib])
+if (env["tools"]!="no"):
+ SConscript('editor/SCsub');
+ #SConscript('scintilla/SCsub');
+ SConscript('collada/SCsub');
+ SConscript('docdump/SCsub');
+ SConscript('freetype/SCsub');
+ SConscript('pe_bliss/SCsub');
+ SConscript('doc/SCsub')
+ SConscript('pck/SCsub')
+ lib = env.Library("tool",env.tool_sources)
+ env.Prepend(LIBS=[lib])
diff --git a/tools/addheader/addheader.py b/tools/addheader/addheader.py
index 6870a1e926..038cec96d0 100644
--- a/tools/addheader/addheader.py
+++ b/tools/addheader/addheader.py
@@ -6,7 +6,7 @@ header="""\
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/collada/SCsub b/tools/collada/SCsub
index c8eaa596d1..34524f10ef 100644
--- a/tools/collada/SCsub
+++ b/tools/collada/SCsub
@@ -3,5 +3,3 @@ Import('env')
env.add_source_files(env.tool_sources,"*.cpp")
Export('env')
-
-
diff --git a/tools/collada/collada.cpp b/tools/collada/collada.cpp
index 97e9f5c36d..deec5f60c7 100644
--- a/tools/collada/collada.cpp
+++ b/tools/collada/collada.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -817,7 +817,7 @@ void Collada::_parse_camera(XMLParser& parser) {
if (name=="perspective") {
camera.mode=CameraData::MODE_PERSPECTIVE;
- } else if (name=="orthogonal") {
+ } else if (name=="orthographic") {
camera.mode=CameraData::MODE_ORTHOGONAL;
} else if (name=="xfov") {
@@ -979,7 +979,7 @@ void Collada::_parse_curve_geometry(XMLParser& parser,String p_id,String p_name)
current_source=id;
COLLADA_PRINT("source data: "+id);
- } else if (section=="float_array" || section=="array" || section=="float_array") {
+ } else if (section=="float_array" || section=="array") {
// create a new array and read it.
if (curvedata.sources.has(current_source)) {
@@ -2054,8 +2054,8 @@ void Collada::_parse_animation(XMLParser& parser) {
}
if (target.find("/")!=-1) { //transform component
- track.target=target.get_slice("/",0);
- track.param=target.get_slice("/",1);
+ track.target=target.get_slicec('/',0);
+ track.param=target.get_slicec('/',1);
if (track.param.find(".")!=-1)
track.component=track.param.get_slice(".",1).to_upper();
track.param=track.param.get_slice(".",0);
diff --git a/tools/collada/collada.h b/tools/collada/collada.h
index f523d24e02..81f51a1f3d 100644
--- a/tools/collada/collada.h
+++ b/tools/collada/collada.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -337,6 +337,24 @@ public:
if(normal==p_vert.normal) {
if(uv==p_vert.uv) {
if(uv2==p_vert.uv2) {
+
+ if (!weights.empty() || !p_vert.weights.empty()) {
+
+ if (weights.size()==p_vert.weights.size()) {
+
+ for(int i=0;i<weights.size();i++) {
+ if (weights[i].bone_idx!=p_vert.weights[i].bone_idx)
+ return weights[i].bone_idx<p_vert.weights[i].bone_idx;
+
+ if (weights[i].weight!=p_vert.weights[i].weight)
+ return weights[i].weight<p_vert.weights[i].weight;
+ }
+ } else {
+ return weights.size() < p_vert.weights.size();
+ }
+
+ }
+
return (color<p_vert.color);
} else
return (uv2<p_vert.uv2);
diff --git a/tools/doc/SCsub b/tools/doc/SCsub
index c8eaa596d1..34524f10ef 100644
--- a/tools/doc/SCsub
+++ b/tools/doc/SCsub
@@ -3,5 +3,3 @@ Import('env')
env.add_source_files(env.tool_sources,"*.cpp")
Export('env')
-
-
diff --git a/tools/doc/doc_data.cpp b/tools/doc/doc_data.cpp
index d13c7ea73c..c1d3e5e314 100644
--- a/tools/doc/doc_data.cpp
+++ b/tools/doc/doc_data.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -59,6 +59,9 @@ void DocData::merge_from(const DocData& p_data) {
if (cf.methods[j].name!=m.name)
continue;
+ if (cf.methods[j].arguments.size()!=m.arguments.size())
+ continue;
+
const MethodDoc &mf = cf.methods[j];
m.description=mf.description;
@@ -134,9 +137,9 @@ void DocData::merge_from(const DocData& p_data) {
void DocData::generate(bool p_basic_types) {
- List<String> classes;
+ List<StringName> classes;
ObjectTypeDB::get_type_list(&classes);
- classes.sort();
+ classes.sort_custom<StringName::AlphCompare>();
while(classes.size()) {
@@ -186,8 +189,12 @@ void DocData::generate(bool p_basic_types) {
arginfo=E->get().return_val;
if (arginfo.type==Variant::NIL)
continue;
-
- method.return_type=(arginfo.hint==PROPERTY_HINT_RESOURCE_TYPE)?arginfo.hint_string:Variant::get_type_name(arginfo.type);
+#ifdef DEBUG_METHODS_ENABLED
+ if (m && m->get_return_type()!=StringName())
+ method.return_type=m->get_return_type();
+ else
+#endif
+ method.return_type=(arginfo.hint==PROPERTY_HINT_RESOURCE_TYPE)?arginfo.hint_string:Variant::get_type_name(arginfo.type);
} else {
@@ -542,7 +549,7 @@ void DocData::generate(bool p_basic_types) {
Globals::Singleton &s=E->get();
pd.name=s.name;
pd.type=s.ptr->get_type();
- while (ObjectTypeDB::type_inherits_from(pd.type)!="Object")
+ while (String(ObjectTypeDB::type_inherits_from(pd.type))!="Object")
pd.type=ObjectTypeDB::type_inherits_from(pd.type);
if (pd.type.begins_with("_"))
pd.type=pd.type.substr(1,pd.type.length());
@@ -912,9 +919,9 @@ Error DocData::save(const String& p_path) {
String qualifiers;
if (m.qualifiers!="")
- qualifiers+="qualifiers=\""+m.qualifiers.xml_escape()+"\"";
+ qualifiers+=" qualifiers=\""+m.qualifiers.xml_escape()+"\"";
- _write_string(f,2,"<method name=\""+m.name+"\" "+qualifiers+" >");
+ _write_string(f,2,"<method name=\""+m.name+"\""+qualifiers+">");
if (m.return_type!="") {
diff --git a/tools/doc/doc_data.h b/tools/doc/doc_data.h
index 018bd67aaf..b62eb21b4a 100644
--- a/tools/doc/doc_data.h
+++ b/tools/doc/doc_data.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/docdump/SCsub b/tools/docdump/SCsub
index c8eaa596d1..34524f10ef 100644
--- a/tools/docdump/SCsub
+++ b/tools/docdump/SCsub
@@ -3,5 +3,3 @@ Import('env')
env.add_source_files(env.tool_sources,"*.cpp")
Export('env')
-
-
diff --git a/tools/docdump/class_list.xml b/tools/docdump/class_list.xml
index ab33cef7d7..3d07f84177 100644
--- a/tools/docdump/class_list.xml
+++ b/tools/docdump/class_list.xml
@@ -3140,7 +3140,7 @@
<constant name="MODE_OPEN_DIR" value="1">
</constant>
<constant name="MODE_OPEN_FILE" value="0">
- Editor will not allow to select unexisting files.
+ Editor will not allow to select nonexistent files.
</constant>
<constant name="MODE_SAVE_FILE" value="2">
Editor will warn when a file exists.
diff --git a/tools/docdump/doc_dump.cpp b/tools/docdump/doc_dump.cpp
index 9fece38995..5f108ee9c8 100644
--- a/tools/docdump/doc_dump.cpp
+++ b/tools/docdump/doc_dump.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -65,7 +65,7 @@ static String _escape_string(const String& p_str) {
ret=ret.replace(">","&lt;");
ret=ret.replace("'","&apos;");
ret=ret.replace("\"","&quot;");
- for (int i=1;i<32;i++) {
+ for (char i=1;i<32;i++) {
char chr[2]={i,0};
ret=ret.replace(chr,"&#"+String::num(i)+";");
@@ -76,10 +76,10 @@ static String _escape_string(const String& p_str) {
void DocDump::dump(const String& p_file) {
- List<String> class_list;
+ List<StringName> class_list;
ObjectTypeDB::get_type_list(&class_list);
- class_list.sort();
+ class_list.sort_custom<StringName::AlphCompare>();
FileAccess *f = FileAccess::open(p_file,FileAccess::WRITE);
diff --git a/tools/docdump/doc_dump.h b/tools/docdump/doc_dump.h
index 1f2f8fb484..cb18289e31 100644
--- a/tools/docdump/doc_dump.h
+++ b/tools/docdump/doc_dump.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/docdump/locales/es/LC_MESSAGES/makedocs.mo b/tools/docdump/locales/es/LC_MESSAGES/makedocs.mo
new file mode 100644
index 0000000000..8d7ea2689e
--- /dev/null
+++ b/tools/docdump/locales/es/LC_MESSAGES/makedocs.mo
Binary files differ
diff --git a/tools/docdump/locales/es/LC_MESSAGES/makedocs.po b/tools/docdump/locales/es/LC_MESSAGES/makedocs.po
new file mode 100644
index 0000000000..82115dd897
--- /dev/null
+++ b/tools/docdump/locales/es/LC_MESSAGES/makedocs.po
@@ -0,0 +1,142 @@
+# Translations template for PROJECT.
+# Copyright (C) 2015 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: makedocs\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2015-10-07 11:47-0600\n"
+"PO-Revision-Date: 2015-10-07 13:10-0600\n"
+"Last-Translator: Jorge Araya Navarro <elcorreo@deshackra.com>\n"
+"Language-Team: \n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.0\n"
+"X-Generator: Poedit 1.8.4\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: makedocs.py:74
+msgid ""
+"\"<code>{gclass}</code>(Go to page of class {gclass})\":/class_{lkclass}"
+msgstr ""
+"\"<code>{gclass}</code>(Ir a la pagina de la clase {gclass})\":/"
+"class_{lkclass}"
+
+#: makedocs.py:76
+msgid ""
+"\"<code>{gclass}.{method}</code>(Go to page {gclass}, section {method})\":/"
+"class_{lkclass}#{lkmethod}"
+msgstr ""
+"\"<code>{gclass}.{method}</code>(Ir a la pagina {gclass}, sección "
+"{method})\":/class_{lkclass}#{lkmethod}"
+
+#: makedocs.py:79
+msgid "\"<code>{method}</code>(Jump to method {method})\":#{lkmethod}"
+msgstr "\"<code>{method}</code>(Saltar al método {method})\":#{lkmethod}"
+
+#: makedocs.py:81
+msgid " \"{rtype}(Go to page of class {rtype})\":/class_{link} "
+msgstr " \"{rtype}(Ir a la pagina de la clase {rtype})\":/class_{link} "
+
+#: makedocs.py:82
+msgid ""
+"\"*{funcname}*(Jump to description for node {funcname})\":#{link} <b>(</b> "
+msgstr ""
+"\"*{funcname}*(Saltar a la descripción para el nodo {funcname})\":#{link} "
+"<b>(</b> "
+
+#: makedocs.py:87
+msgid "h4. Inherits: "
+msgstr "h4. Hereda de: "
+
+#: makedocs.py:232
+msgid "<doc>'s version attribute missing"
+msgstr "El atributo version de <doc> no existe"
+
+#: makedocs.py:246
+msgid "|_. Index symbol |_. Class name |_. Index symbol |_. Class name |\n"
+msgstr ""
+"|_. Índice de símbolo |_. Nombre de la clase |_. Índice de símbolo |_. "
+"Nombre de la clase |\n"
+
+#: makedocs.py:305
+msgid ""
+"h4. Category: {}\n"
+"\n"
+msgstr ""
+"h4. Categoría: {}\n"
+"\n"
+
+#: makedocs.py:310
+msgid ""
+"h2. Brief Description\n"
+"\n"
+msgstr ""
+"h2. Descripción breve\n"
+"\n"
+
+#: makedocs.py:312
+msgid ""
+"\"read more\":#more\n"
+"\n"
+msgstr ""
+"\"Leer más\":#more\n"
+"\n"
+
+#: makedocs.py:317
+msgid ""
+"\n"
+"h3. Member Functions\n"
+"\n"
+msgstr ""
+"\n"
+"h3. Funciones miembro\n"
+"\n"
+
+#: makedocs.py:323
+msgid ""
+"\n"
+"h3. Signals\n"
+"\n"
+msgstr ""
+"\n"
+"h3. Señales\n"
+"\n"
+
+#: makedocs.py:331
+msgid ""
+"\n"
+"h3. Numeric Constants\n"
+"\n"
+msgstr ""
+"\n"
+"h3. Constantes numéricas\n"
+"\n"
+
+#: makedocs.py:347
+msgid ""
+"\n"
+"h3(#more). Description\n"
+"\n"
+msgstr ""
+"\n"
+"h3(#more). Descripción\n"
+"\n"
+
+#: makedocs.py:351
+msgid "_Nothing here, yet..._\n"
+msgstr "_Aún nada por aquí..._\n"
+
+#: makedocs.py:355
+msgid ""
+"\n"
+"h3. Member Function Description\n"
+"\n"
+msgstr ""
+"\n"
+"h3. Descripción de las funciones miembro\n"
+"\n"
diff --git a/tools/docdump/makedocs.pot b/tools/docdump/makedocs.pot
new file mode 100644
index 0000000000..be3220f686
--- /dev/null
+++ b/tools/docdump/makedocs.pot
@@ -0,0 +1,108 @@
+# Translations template for PROJECT.
+# Copyright (C) 2015 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: makedocs 0.1\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2015-10-07 11:47-0600\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.0\n"
+"X-Generator: Poedit 1.8.4\n"
+
+#: makedocs.py:74
+msgid "\"<code>{gclass}</code>(Go to page of class {gclass})\":/class_{lkclass}"
+msgstr ""
+
+#: makedocs.py:76
+msgid "\"<code>{gclass}.{method}</code>(Go to page {gclass}, section {method})\":/class_{lkclass}#{lkmethod}"
+msgstr ""
+
+#: makedocs.py:79
+msgid "\"<code>{method}</code>(Jump to method {method})\":#{lkmethod}"
+msgstr ""
+
+#: makedocs.py:81
+msgid " \"{rtype}(Go to page of class {rtype})\":/class_{link} "
+msgstr ""
+
+#: makedocs.py:82
+msgid "\"*{funcname}*(Jump to description for node {funcname})\":#{link} <b>(</b> "
+msgstr ""
+
+#: makedocs.py:87
+msgid "h4. Inherits: "
+msgstr ""
+
+#: makedocs.py:232
+msgid "<doc>'s version attribute missing"
+msgstr ""
+
+#: makedocs.py:246
+msgid "|_. Index symbol |_. Class name |_. Index symbol |_. Class name |\n"
+msgstr ""
+
+#: makedocs.py:305
+msgid ""
+"h4. Category: {}\n"
+"\n"
+msgstr ""
+
+#: makedocs.py:310
+msgid ""
+"h2. Brief Description\n"
+"\n"
+msgstr ""
+
+#: makedocs.py:312
+msgid ""
+"\"read more\":#more\n"
+"\n"
+msgstr ""
+
+#: makedocs.py:317
+msgid ""
+"\n"
+"h3. Member Functions\n"
+"\n"
+msgstr ""
+
+#: makedocs.py:323
+msgid ""
+"\n"
+"h3. Signals\n"
+"\n"
+msgstr ""
+
+#: makedocs.py:331
+msgid ""
+"\n"
+"h3. Numeric Constants\n"
+"\n"
+msgstr ""
+
+#: makedocs.py:347
+msgid ""
+"\n"
+"h3(#more). Description\n"
+"\n"
+msgstr ""
+
+#: makedocs.py:351
+msgid "_Nothing here, yet..._\n"
+msgstr ""
+
+#: makedocs.py:355
+msgid ""
+"\n"
+"h3. Member Function Description\n"
+"\n"
+msgstr ""
diff --git a/tools/docdump/makedocs.py b/tools/docdump/makedocs.py
new file mode 100644
index 0000000000..be57891abc
--- /dev/null
+++ b/tools/docdump/makedocs.py
@@ -0,0 +1,382 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+#
+# makedocs.py: Generate documentation for Open Project Wiki
+# Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur.
+# Contributor: Jorge Araya Navarro <elcorreo@deshackra.com>
+#
+
+# IMPORTANT NOTICE:
+# If you are going to modify anything from this file, please be sure to follow
+# the Style Guide for Python Code or often called "PEP8". To do this
+# automagically just install autopep8:
+#
+# $ sudo pip3 install autopep8
+#
+# and run:
+#
+# $ autopep8 makedocs.py
+#
+# Before committing your changes. Also be sure to delete any trailing
+# whitespace you may left.
+#
+# TODO:
+# * Refactor code.
+# * Adapt this script for generating content in other markup formats like
+# DokuWiki, Markdown, etc.
+#
+# Also check other TODO entries in this script for more information on what is
+# left to do.
+import argparse
+import gettext
+import logging
+import re
+from itertools import zip_longest
+from os import path, listdir
+from xml.etree import ElementTree
+
+
+# add an option to change the verbosity
+logging.basicConfig(level=logging.INFO)
+
+
+def getxmlfloc():
+ """ Returns the supposed location of the XML file
+ """
+ filepath = path.dirname(path.abspath(__file__))
+ return path.join(filepath, "class_list.xml")
+
+
+def langavailable():
+ """ Return a list of languages available for translation
+ """
+ filepath = path.join(
+ path.dirname(path.abspath(__file__)), "locales")
+ files = listdir(filepath)
+ choices = [x for x in files]
+ choices.insert(0, "none")
+ return choices
+
+
+desc = "Generates documentation from a XML file to different markup languages"
+
+parser = argparse.ArgumentParser(description=desc)
+parser.add_argument("--input", dest="xmlfp", default=getxmlfloc(),
+ help="Input XML file, default: {}".format(getxmlfloc()))
+parser.add_argument("--output-dir", dest="outputdir", required=True,
+ help="Output directory for generated files")
+parser.add_argument("--language", choices=langavailable(), default="none",
+ help=("Choose the language of translation"
+ " for the output files. Default is English (none). "
+ "Note: This is NOT for the documentation itself!"))
+# TODO: add an option for outputting different markup formats
+
+args = parser.parse_args()
+# Let's check if the file and output directory exists
+if not path.isfile(args.xmlfp):
+ logging.critical("File not found: {}".format(args.xmlfp))
+ exit(1)
+elif not path.isdir(args.outputdir):
+ logging.critical("Path does not exist: {}".format(args.outputdir))
+ exit(1)
+
+_ = gettext.gettext
+if args.language != "none":
+ lang = gettext.translation(domain="makedocs",
+ localedir="locales",
+ languages=[args.language])
+ lang.install()
+
+ _ = lang.gettext
+
+# Strings
+C_LINK = _("\"<code>{gclass}</code>(Go to page of class"
+ " {gclass})\":/class_{lkclass}")
+MC_LINK = _("\"<code>{gclass}.{method}</code>(Go "
+ "to page {gclass}, section {method})\""
+ ":/class_{lkclass}#{lkmethod}")
+TM_JUMP = _("\"<code>{method}</code>(Jump to method"
+ " {method})\":#{lkmethod}")
+GTC_LINK = _(" \"{rtype}(Go to page of class {rtype})\":/class_{link} ")
+DFN_JUMP = _("\"*{funcname}*(Jump to description for"
+ " node {funcname})\":#{link} <b>(</b> ")
+M_ARG_DEFAULT = C_LINK + " {name}={default}"
+M_ARG = C_LINK + " {name}"
+
+OPENPROJ_INH = _("h4. Inherits: ") + C_LINK + "\n\n"
+
+
+def tb(string):
+ """ Return a byte representation of a string
+ """
+ return bytes(string, "UTF-8")
+
+
+def sortkey(c):
+ """ Symbols are first, letters second
+ """
+ if "_" == c.attrib["name"][0]:
+ return "A"
+ else:
+ return c.attrib["name"]
+
+
+def toOP(text):
+ """ Convert commands in text to Open Project commands
+ """
+ # TODO: Make this capture content between [command] ... [/command]
+ groups = re.finditer((r'\[html (?P<command>/?\w+/?)(\]| |=)?(\]| |=)?(?P<a'
+ 'rg>\w+)?(\]| |=)?(?P<value>"[^"]+")?/?\]'), text)
+ alignstr = ""
+ for group in groups:
+ gd = group.groupdict()
+ if gd["command"] == "br/":
+ text = text.replace(group.group(0), "\n\n", 1)
+ elif gd["command"] == "div":
+ if gd["value"] == '"center"':
+ alignstr = ("{display:block; margin-left:auto;"
+ " margin-right:auto;}")
+ elif gd["value"] == '"left"':
+ alignstr = "<"
+ elif gd["value"] == '"right"':
+ alignstr = ">"
+ text = text.replace(group.group(0), "\n\n", 1)
+ elif gd["command"] == "/div":
+ alignstr = ""
+ text = text.replace(group.group(0), "\n\n", 1)
+ elif gd["command"] == "img":
+ text = text.replace(group.group(0), "!{align}{src}!".format(
+ align=alignstr, src=gd["value"].strip('"')), 1)
+ elif gd["command"] == "b" or gd["command"] == "/b":
+ text = text.replace(group.group(0), "*", 1)
+ elif gd["command"] == "i" or gd["command"] == "/i":
+ text = text.replace(group.group(0), "_", 1)
+ elif gd["command"] == "u" or gd["command"] == "/u":
+ text = text.replace(group.group(0), "+", 1)
+ # Process other non-html commands
+ groups = re.finditer((r'\[method ((?P<class>[aA0-zZ9_]+)(?:\.))'
+ r'?(?P<method>[aA0-zZ9_]+)\]'), text)
+ for group in groups:
+ gd = group.groupdict()
+ if gd["class"]:
+ replacewith = (MC_LINK.format(gclass=gd["class"],
+ method=gd["method"],
+ lkclass=gd["class"].lower(),
+ lkmethod=gd["method"].lower()))
+ else:
+ # The method is located in the same wiki page
+ replacewith = (TM_JUMP.format(method=gd["method"],
+ lkmethod=gd["method"].lower()))
+
+ text = text.replace(group.group(0), replacewith, 1)
+ # Finally, [Classes] are around brackets, make them direct links
+ groups = re.finditer(r'\[(?P<class>[az0-AZ0_]+)\]', text)
+ for group in groups:
+ gd = group.groupdict()
+ replacewith = (C_LINK.
+ format(gclass=gd["class"],
+ lkclass=gd["class"].lower()))
+ text = text.replace(group.group(0), replacewith, 1)
+
+ return text + "\n\n"
+
+
+def mkfn(node, is_signal=False):
+ """ Return a string containing a unsorted item for a function
+ """
+ finalstr = ""
+ name = node.attrib["name"]
+ rtype = node.find("return")
+ if rtype:
+ rtype = rtype.attrib["type"]
+ else:
+ rtype = "void"
+ # write the return type and the function name first
+ finalstr += "* "
+ # return type
+ if not is_signal:
+ if rtype != "void":
+ finalstr += GTC_LINK.format(
+ rtype=rtype,
+ link=rtype.lower())
+ else:
+ finalstr += " void "
+
+ # function name
+ if not is_signal:
+ finalstr += DFN_JUMP.format(
+ funcname=name,
+ link=name.lower())
+ else:
+ # Signals have no description
+ finalstr += "*{funcname}* <b>(</b>".format(funcname=name)
+ # loop for the arguments of the function, if any
+ args = []
+ for arg in sorted(
+ node.iter(tag="argument"),
+ key=lambda a: int(a.attrib["index"])):
+
+ ntype = arg.attrib["type"]
+ nname = arg.attrib["name"]
+
+ if "default" in arg.attrib:
+ args.insert(-1, M_ARG_DEFAULT.format(
+ gclass=ntype,
+ lkclass=ntype.lower(),
+ name=nname,
+ default=arg.attrib["default"]))
+ else:
+ # No default value present
+ args.insert(-1, M_ARG.format(gclass=ntype,
+ lkclass=ntype.lower(), name=nname))
+ # join the arguments together
+ finalstr += ", ".join(args)
+ # and, close the function with a )
+ finalstr += " <b>)</b>"
+ # write the qualifier, if any
+ if "qualifiers" in node.attrib:
+ qualifier = node.attrib["qualifiers"]
+ finalstr += " " + qualifier
+
+ finalstr += "\n"
+
+ return finalstr
+
+# Let's begin
+tree = ElementTree.parse(args.xmlfp)
+root = tree.getroot()
+
+# Check version attribute exists in <doc>
+if "version" not in root.attrib:
+ logging.critical(_("<doc>'s version attribute missing"))
+ exit(1)
+
+version = root.attrib["version"]
+classes = sorted(root, key=sortkey)
+# first column is always longer, second column of classes should be shorter
+zclasses = zip_longest(classes[:int(len(classes) / 2 + 1)],
+ classes[int(len(classes) / 2 + 1):],
+ fillvalue="")
+
+# We write the class_list file and also each class file at once
+with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl:
+ # Write header of table
+ fcl.write(tb("|^.\n"))
+ fcl.write(tb(_("|_. Index symbol |_. Class name "
+ "|_. Index symbol |_. Class name |\n")))
+ fcl.write(tb("|-.\n"))
+
+ indexletterl = ""
+ indexletterr = ""
+ for gdclassl, gdclassr in zclasses:
+ # write a row #
+ # write the index symbol column, left
+ if indexletterl != gdclassl.attrib["name"][0]:
+ indexletterl = gdclassl.attrib["name"][0]
+ fcl.write(tb("| *{}* |".format(indexletterl.upper())))
+ else:
+ # empty cell
+ fcl.write(tb("| |"))
+ # write the class name column, left
+ fcl.write(tb(C_LINK.format(
+ gclass=gdclassl.attrib["name"],
+ lkclass=gdclassl.attrib["name"].lower())))
+
+ # write the index symbol column, right
+ if isinstance(gdclassr, ElementTree.Element):
+ if indexletterr != gdclassr.attrib["name"][0]:
+ indexletterr = gdclassr.attrib["name"][0]
+ fcl.write(tb("| *{}* |".format(indexletterr.upper())))
+ else:
+ # empty cell
+ fcl.write(tb("| |"))
+ # We are dealing with an empty string
+ else:
+ # two empty cell
+ fcl.write(tb("| | |\n"))
+ # We won't get the name of the class since there is no ElementTree
+ # object for the right side of the tuple, so we iterate the next
+ # tuple instead
+ continue
+
+ # write the class name column (if any), right
+ fcl.write(tb(C_LINK.format(
+ gclass=gdclassl.attrib["name"],
+ lkclass=gdclassl.attrib["name"].lower()) + "|\n"))
+
+ # row written #
+ # now, let's write each class page for each class
+ for gdclass in [gdclassl, gdclassr]:
+ if not isinstance(gdclass, ElementTree.Element):
+ continue
+
+ classname = gdclass.attrib["name"]
+ with open(path.join(args.outputdir, "{}.txt".format(
+ classname.lower())), "wb") as clsf:
+ # First level header with the name of the class
+ clsf.write(tb("h1. {}\n\n".format(classname)))
+ # lay the attributes
+ if "inherits" in gdclass.attrib:
+ inh = gdclass.attrib["inherits"].strip()
+ clsf.write(tb(OPENPROJ_INH.format(gclass=inh,
+ lkclass=inh.lower())))
+ if "category" in gdclass.attrib:
+ clsf.write(tb(_("h4. Category: {}\n\n").
+ format(gdclass.attrib["category"].strip())))
+ # lay child nodes
+ briefd = gdclass.find("brief_description")
+ if briefd.text.strip():
+ clsf.write(tb(_("h2. Brief Description\n\n")))
+ clsf.write(tb(toOP(briefd.text.strip()) +
+ _("\"read more\":#more\n\n")))
+
+ # Write the list of member functions of this class
+ methods = gdclass.find("methods")
+ if methods and len(methods) > 0:
+ clsf.write(tb(_("\nh3. Member Functions\n\n")))
+ for method in methods.iter(tag='method'):
+ clsf.write(tb(mkfn(method)))
+
+ signals = gdclass.find("signals")
+ if signals and len(signals) > 0:
+ clsf.write(tb(_("\nh3. Signals\n\n")))
+ for signal in signals.iter(tag='signal'):
+ clsf.write(tb(mkfn(signal, True)))
+ # TODO: <members> tag is necessary to process? it does not
+ # exists in class_list.xml file.
+
+ consts = gdclass.find("constants")
+ if consts and len(consts) > 0:
+ clsf.write(tb(_("\nh3. Numeric Constants\n\n")))
+ for const in sorted(consts, key=lambda k:
+ k.attrib["name"]):
+ if const.text.strip():
+ clsf.write(tb("* *{name}* = *{value}* - {desc}\n".
+ format(
+ name=const.attrib["name"],
+ value=const.attrib["value"],
+ desc=const.text.strip())))
+ else:
+ # Constant have no description
+ clsf.write(tb("* *{name}* = *{value}*\n".
+ format(
+ name=const.attrib["name"],
+ value=const.attrib["value"])))
+ descrip = gdclass.find("description")
+ clsf.write(tb(_("\nh3(#more). Description\n\n")))
+ if descrip.text:
+ clsf.write(tb(descrip.text.strip() + "\n"))
+ else:
+ clsf.write(tb(_("_Nothing here, yet..._\n")))
+
+ # and finally, the description for each method
+ if methods and len(methods) > 0:
+ clsf.write(tb(_("\nh3. Member Function Description\n\n")))
+ for method in methods.iter(tag='method'):
+ clsf.write(tb("h4(#{n}). {name}\n\n".format(
+ n=method.attrib["name"].lower(),
+ name=method.attrib["name"])))
+ clsf.write(tb(mkfn(method) + "\n"))
+ clsf.write(tb(toOP(method.find(
+ "description").text.strip())))
diff --git a/tools/docdump/makehtml.py b/tools/docdump/makehtml.py
index d533ca1b8b..9b9c62f33b 100644
--- a/tools/docdump/makehtml.py
+++ b/tools/docdump/makehtml.py
@@ -1,5 +1,19 @@
import sys
import xml.etree.ElementTree as ET
+from xml.sax.saxutils import escape, unescape
+
+html_escape_table = {
+ '"': "&quot;",
+ "'": "&apos;"
+}
+
+html_unescape_table = {v:k for k, v in html_escape_table.items()}
+
+def html_escape(text):
+ return escape(text, html_escape_table)
+
+def html_unescape(text):
+ return unescape(text, html_unescape_table)
input_list = []
@@ -96,7 +110,7 @@ def make_html_class_list(class_list,columns):
idx=0
for n in class_list:
- col = idx/col_max
+ col = int(idx/col_max)
if (col>=columns):
col=columns-1
fit_columns[col]+=[n]
@@ -299,6 +313,7 @@ def make_type(p_type,p_parent):
def make_text_def(class_name,parent,text):
+ text = html_escape(text)
pos=0
while(True):
pos = text.find("[",pos)
@@ -598,7 +613,6 @@ def make_html_class(node):
descr=node.find("description")
if (descr!=None and descr.text.strip()!=""):
-
h4=ET.SubElement(div,"h4")
h4.text="Description:"
@@ -644,7 +658,6 @@ def make_html_class(node):
class_names=[]
classes={}
-
for file in input_list:
tree = ET.parse(file)
doc=tree.getroot()
diff --git a/tools/editor/SCsub b/tools/editor/SCsub
index 73ec530177..cd46ff8353 100644
--- a/tools/editor/SCsub
+++ b/tools/editor/SCsub
@@ -28,7 +28,7 @@ def make_doc_header(target,source,env):
-
+
if (env["tools"]=="yes"):
@@ -43,17 +43,16 @@ if (env["tools"]=="yes"):
f.write(reg_exporters_inc)
f.write(reg_exporters)
f.close()
-
+
env.Depends("#tools/editor/doc_data_compressed.h","#doc/base/classes.xml")
env.Command("#tools/editor/doc_data_compressed.h","#doc/base/classes.xml",make_doc_header)
#make_doc_header(env.File("#tools/editor/doc_data_raw.h").srcnode().abspath,env.File("#doc/base/classes.xml").srcnode().abspath,env)
-
+
env.add_source_files(env.tool_sources,"*.cpp")
-
+
Export('env')
- SConscript('icons/SCsub');
+ SConscript('icons/SCsub');
SConscript('plugins/SCsub');
SConscript('fileserver/SCsub');
SConscript('io_plugins/SCsub');
-
diff --git a/tools/editor/animation_editor.cpp b/tools/editor/animation_editor.cpp
index 95f9ee6509..ace6fda696 100644
--- a/tools/editor/animation_editor.cpp
+++ b/tools/editor/animation_editor.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,6 +33,7 @@
#include "io/resource_saver.h"
#include "pair.h"
#include "scene/gui/separator.h"
+#include "editor_node.h"
/* Missing to fix:
*Set
@@ -44,11 +45,207 @@
*/
+class AnimationCurveEdit : public Control {
+ OBJ_TYPE( AnimationCurveEdit, Control );
+public:
+ enum Mode {
+ MODE_DISABLED,
+ MODE_SINGLE,
+ MODE_MULTIPLE
+ };
+private:
+
+ Set<float> multiples;
+ float transition;
+ Mode mode;
+
+ void _notification(int p_what) {
+
+ if (p_what==NOTIFICATION_DRAW) {
+
+
+ RID ci = get_canvas_item();
+
+ Size2 s = get_size();
+ Rect2 r(Point2(),s);
+
+ //r=r.grow(3);
+ Ref<StyleBox> sb = get_stylebox("normal","LineEdit");
+ sb->draw(ci,r);
+ r.size-=sb->get_minimum_size();
+ r.pos+=sb->get_offset();
+ //VisualServer::get_singleton()->canvas_item_add
+
+ Ref<Font> f = get_font("font","Label");
+ r=r.grow(-2);
+ Color color = get_color("font_color","Label");
+
+ int points = 48;
+ if (mode==MODE_MULTIPLE) {
+
+ int max_draw = 16;
+ Color mcolor=color;
+ mcolor.a*=0.3;
+
+ Set<float>::Element *E=multiples.front();
+ for(int j=0;j<16;j++) {
+
+ if (!E)
+ break;
+
+ float prev=1.0;
+ float exp=E->get();
+ bool flip=false;//hint_text=="attenuation";
+
+
+ for(int i=1;i<=points;i++) {
+
+ float ifl = i/float(points);
+ float iflp = (i-1)/float(points);
+
+ float h = 1.0-Math::ease(ifl,exp);
+
+ if (flip) {
+ ifl=1.0-ifl;
+ iflp=1.0-iflp;
+ }
+
+ VisualServer::get_singleton()->canvas_item_add_line(ci,r.pos+Point2(iflp*r.size.width,prev*r.size.height),r.pos+Point2(ifl*r.size.width,h*r.size.height),mcolor);
+ prev=h;
+ }
+
+ E=E->next();
+ }
+ }
+
+ float exp=transition;
+ if (mode!=MODE_DISABLED) {
+
+
+ float prev=1.0;
+
+ bool flip=false;//hint_text=="attenuation";
+
+
+ for(int i=1;i<=points;i++) {
+
+ float ifl = i/float(points);
+ float iflp = (i-1)/float(points);
+
+ float h = 1.0-Math::ease(ifl,exp);
+
+ if (flip) {
+ ifl=1.0-ifl;
+ iflp=1.0-iflp;
+ }
+
+ VisualServer::get_singleton()->canvas_item_add_line(ci,r.pos+Point2(iflp*r.size.width,prev*r.size.height),r.pos+Point2(ifl*r.size.width,h*r.size.height),color);
+ prev=h;
+ }
+ }
+
+ String txt=String::num(exp,2);
+ if (mode==MODE_DISABLED) {
+ txt="Disabled";
+ } else if (mode==MODE_MULTIPLE) {
+ txt+=" - All Selection";
+ }
+
+ f->draw(ci,Point2(10,10+f->get_ascent()),txt,color);
+
+ }
+ }
+
+ void _input_event(const InputEvent& p_ev) {
+ if (p_ev.type==InputEvent::MOUSE_MOTION && p_ev.mouse_motion.button_mask&BUTTON_MASK_LEFT) {
+
+ if (mode==MODE_DISABLED)
+ return;
+
+ float rel = p_ev.mouse_motion.relative_x;
+ if (rel==0)
+ return;
+
+ bool flip=false;
+
+ if (flip)
+ rel=-rel;
+
+ float val = transition;
+ if (val==0)
+ return;
+ bool sg = val < 0;
+ val = Math::absf(val);
+
+ val = Math::log(val)/Math::log(2);
+ //logspace
+ val+=rel*0.05;
+ //
+
+ val = Math::pow(2,val);
+ if (sg)
+ val=-val;
+
+ transition=val;
+ update();
+ //emit_signal("variant_changed");
+ emit_signal("transition_changed",transition);
+ }
+ }
+
+public:
+
+ static void _bind_methods() {
+
+ // ObjectTypeDB::bind_method("_update_obj",&AnimationKeyEdit::_update_obj);
+ ObjectTypeDB::bind_method("_input_event",&AnimationCurveEdit::_input_event);
+ ADD_SIGNAL(MethodInfo("transition_changed"));
+ }
+
+ void set_mode(Mode p_mode) {
+
+ mode=p_mode;
+ update();
+ }
+
+ void clear_multiples() { multiples.clear(); update();}
+ void set_multiple(float p_transition) {
+
+ multiples.insert(p_transition);
+ }
+
+ void set_transition(float p_transition) {
+ transition=p_transition;
+ update();
+ }
+
+ float get_transition() const {
+ return transition;
+ }
+
+ void force_transition(float p_value) {
+ if (mode==MODE_DISABLED)
+ return;
+ transition=p_value;
+ emit_signal("transition_changed",p_value);
+ update();
+ }
+
+ AnimationCurveEdit() {
+
+ transition=1.0;
+ set_default_cursor_shape(CURSOR_HSPLIT);
+ mode=MODE_DISABLED;
+ }
+
+};
+
class AnimationKeyEdit : public Object {
OBJ_TYPE(AnimationKeyEdit,Object);
public:
bool setting;
+ bool hidden;
static void _bind_methods() {
@@ -56,12 +253,12 @@ public:
ObjectTypeDB::bind_method("_key_ofs_changed",&AnimationKeyEdit::_key_ofs_changed);
}
- PopupDialog *ke_dialog;
+ //PopupDialog *ke_dialog;
void _update_obj(const Ref<Animation> &p_anim) {
if (setting)
return;
- if (!ke_dialog->is_visible())
+ if (hidden)
return;
if (!(animation==p_anim))
return;
@@ -69,7 +266,7 @@ public:
}
void _key_ofs_changed(const Ref<Animation> &p_anim,float from, float to) {
- if (!ke_dialog->is_visible())
+ if (hidden)
return;
if (!(animation==p_anim))
return;
@@ -408,8 +605,8 @@ public:
} break;
}
- if (animation->track_get_type(track)!=Animation::TYPE_METHOD)
- p_list->push_back( PropertyInfo( Variant::REAL, "easing", PROPERTY_HINT_EXP_EASING));
+ //if (animation->track_get_type(track)!=Animation::TYPE_METHOD)
+ // p_list->push_back( PropertyInfo( Variant::REAL, "easing", PROPERTY_HINT_EXP_EASING));
}
UndoRedo *undo_redo;
@@ -425,31 +622,46 @@ public:
_change_notify();
}
- AnimationKeyEdit() { key_ofs=0; track=-1; setting=false; }
+ AnimationKeyEdit() { hidden=true; key_ofs=0; track=-1; setting=false; }
};
-void AnimationKeyEditor::_menu_track(int p_type) {
+void AnimationKeyEditor::_menu_add_track(int p_type) {
ERR_FAIL_COND(!animation.is_valid());
- last_menu_track_opt=p_type;
switch(p_type) {
- case TRACK_MENU_ADD_VALUE_TRACK:
- case TRACK_MENU_ADD_TRANSFORM_TRACK:
- case TRACK_MENU_ADD_CALL_TRACK: {
+ case ADD_TRACK_MENU_ADD_CALL_TRACK: {
+ if (root) {
+ call_select->popup_centered_ratio();
+ break;
+ }
+ } break;
+ case ADD_TRACK_MENU_ADD_VALUE_TRACK:
+ case ADD_TRACK_MENU_ADD_TRANSFORM_TRACK: {
undo_redo->create_action("Anim Add Track");
- undo_redo->add_do_method(animation.ptr(),"add_track",p_type);
+ undo_redo->add_do_method(animation.ptr(),"add_track",p_type);
undo_redo->add_do_method(animation.ptr(),"track_set_path",animation->get_track_count(),".");
undo_redo->add_undo_method(animation.ptr(),"remove_track",animation->get_track_count());
undo_redo->commit_action();
} break;
+ }
+}
+
+void AnimationKeyEditor::_menu_track(int p_type) {
+
+ ERR_FAIL_COND(!animation.is_valid());
+
+
+ last_menu_track_opt=p_type;
+ switch(p_type) {
+
case TRACK_MENU_SCALE:
case TRACK_MENU_SCALE_PIVOT: {
@@ -610,6 +822,8 @@ void AnimationKeyEditor::_menu_track(int p_type) {
selection=new_selection;
track_editor->update();
+ _edit_if_single_selection();
+
}
@@ -689,8 +903,31 @@ void AnimationKeyEditor::_menu_track(int p_type) {
optimize_dialog->popup_centered(Size2(250,180));
} break;
+ case CURVE_SET_LINEAR: {
+ curve_edit->force_transition(1.0);
+
+ } break;
+ case CURVE_SET_IN: {
+
+ curve_edit->force_transition(4.0);
+
+ } break;
+ case CURVE_SET_OUT: {
+ curve_edit->force_transition(0.25);
+ } break;
+ case CURVE_SET_INOUT: {
+ curve_edit->force_transition(-4);
+
+ } break;
+ case CURVE_SET_OUTIN: {
+
+ curve_edit->force_transition(-0.25);
+ } break;
+ case CURVE_SET_CONSTANT: {
+ curve_edit->force_transition(0);
+ } break;
}
@@ -772,19 +1009,24 @@ void AnimationKeyEditor::_track_editor_draw() {
if (!animation.is_valid()) {
v_scroll->hide();
h_scroll->hide();
+ menu_add_track->set_disabled(true);
menu_track->set_disabled(true);
edit_button->set_disabled(true);
+ key_editor_tab->hide();
move_up_button->set_disabled(true);
move_down_button->set_disabled(true);
remove_button->set_disabled(true);
return;
}
+ menu_add_track->set_disabled(false);
menu_track->set_disabled(false);
edit_button->set_disabled(false);
move_up_button->set_disabled(false);
move_down_button->set_disabled(false);
remove_button->set_disabled(false);
+ if (edit_button->is_pressed())
+ key_editor_tab->show();
te_drawing=true;
@@ -1000,8 +1242,13 @@ void AnimationKeyEditor::_track_editor_draw() {
}
}
+ Color sep_color=color;
+ color.a*=0.5;
+
for(int i=0;i<fit;i++) {
+ //this code sucks, i always forget how it works
+
int idx = v_scroll->get_val() + i;
if (idx>=animation->get_track_count())
break;
@@ -1090,6 +1337,7 @@ void AnimationKeyEditor::_track_editor_draw() {
float key_hofs = -Math::floor(type_icon[tt]->get_height()/2);
int kc=animation->track_get_key_count(idx);
+ bool first=true;
for(int i=0;i<kc;i++) {
@@ -1097,8 +1345,16 @@ void AnimationKeyEditor::_track_editor_draw() {
float time = animation->track_get_key_time(idx,i);
if (time<keys_from)
continue;
- if (time>keys_to)
+ if (time>keys_to) {
+
+ if (first && i>0 && animation->track_get_key_value(idx,i)==animation->track_get_key_value(idx,i-1)) {
+ //draw whole line
+ te->draw_line(ofs+Vector2(name_limit,y+h/2),ofs+Point2(settings_limit,y+h/2),color);
+ }
+
break;
+ }
+
float x = key_hofs + name_limit + (time-keys_from)*zoom_scale;
Ref<Texture> tex = type_icon[tt];
@@ -1116,7 +1372,22 @@ void AnimationKeyEditor::_track_editor_draw() {
if (mouse_over.over==MouseOver::OVER_KEY && mouse_over.track==idx && mouse_over.over_key==i)
tex=type_hover;
+ Variant value = animation->track_get_key_value(idx,i);
+ if (first && i>0 && value==animation->track_get_key_value(idx,i-1)) {
+
+ te->draw_line(ofs+Vector2(name_limit,y+h/2),ofs+Point2(x,y+h/2),color);
+ }
+
+ if (i<kc-1 && value==animation->track_get_key_value(idx,i+1)) {
+ float x_n = key_hofs + name_limit + (animation->track_get_key_time(idx,i+1)-keys_from)*zoom_scale;
+
+ x_n = MIN( x_n, settings_limit);
+ te->draw_line(ofs+Point2(x_n,y+h/2),ofs+Point2(x,y+h/2),color);
+
+ }
+
te->draw_texture(tex,ofs+Point2(x,y+key_vofs).floor());
+ first=false;
}
}
@@ -1226,7 +1497,9 @@ void AnimationKeyEditor::_clear_selection_for_anim(const Ref<Animation>& p_anim)
if (!(animation==p_anim))
return;
- selection.clear();
+ //selection.clear();
+ _clear_selection();
+
}
void AnimationKeyEditor::_select_at_anim(const Ref<Animation>& p_anim,int p_track,float p_pos){
@@ -1244,6 +1517,7 @@ void AnimationKeyEditor::_select_at_anim(const Ref<Animation>& p_anim,int p_trac
ki.pos=p_pos;
selection.insert(sk,ki);
+
}
@@ -1283,6 +1557,83 @@ PropertyInfo AnimationKeyEditor::_find_hint_for_track(int p_idx) {
}
+void AnimationKeyEditor::_curve_transition_changed(float p_what) {
+
+ if (selection.size()==0)
+ return;
+ if (selection.size()==1)
+ undo_redo->create_action("Edit Node Curve",true);
+ else
+ undo_redo->create_action("Edit Selection Curve",true);
+
+ for(Map<SelectedKey,KeyInfo>::Element *E=selection.front();E;E=E->next()) {
+
+ int track = E->key().track;
+ int key = E->key().key;
+ float prev_val = animation->track_get_key_transition(track,key);
+ undo_redo->add_do_method(animation.ptr(),"track_set_key_transition",track,key,p_what);
+ undo_redo->add_undo_method(animation.ptr(),"track_set_key_transition",track,key,prev_val);
+ }
+
+ undo_redo->commit_action();
+
+}
+
+void AnimationKeyEditor::_toggle_edit_curves() {
+
+ if (edit_button->is_pressed())
+ key_editor_tab->show();
+ else
+ key_editor_tab->hide();
+}
+
+
+bool AnimationKeyEditor::_edit_if_single_selection() {
+
+ if (selection.size()!=1) {
+
+ if (selection.size()==0) {
+ curve_edit->set_mode(AnimationCurveEdit::MODE_DISABLED);
+ print_line("disable");
+ } else {
+
+ curve_edit->set_mode(AnimationCurveEdit::MODE_MULTIPLE);
+ curve_edit->set_transition(1.0);
+ curve_edit->clear_multiples();
+ //add all
+ for(Map<SelectedKey,KeyInfo>::Element *E=selection.front();E;E=E->next()) {
+
+ curve_edit->set_multiple(animation->track_get_key_transition(E->key().track,E->key().key));
+ }
+ print_line("multiple");
+
+ }
+ return false;
+ }
+ curve_edit->set_mode(AnimationCurveEdit::MODE_SINGLE);
+ print_line("regular");
+
+ int idx = selection.front()->key().track;
+ int key = selection.front()->key().key;
+ {
+
+ key_edit->animation=animation;
+ key_edit->track=idx;
+ key_edit->key_ofs=animation->track_get_key_time(idx,key);
+ key_edit->hint=_find_hint_for_track(idx);
+ key_edit->notify_change();
+
+ curve_edit->set_transition(animation->track_get_key_transition(idx,key));
+
+ /*key_edit_dialog->set_size( Size2( 200,200) );
+ key_edit_dialog->set_pos( track_editor->get_global_pos() + ofs + mpos +Point2(-100,20));
+ key_edit_dialog->popup();*/
+
+ }
+
+ return true;
+
+}
void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
@@ -1364,9 +1715,12 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
undo_redo->add_undo_method(animation.ptr(),"track_insert_key",E->key().track,E->get().pos,animation->track_get_key_value(E->key().track,E->key().key),animation->track_get_key_transition(E->key().track,E->key().key));
}
+ undo_redo->add_do_method(this,"_clear_selection_for_anim",animation);
+ undo_redo->add_undo_method(this,"_clear_selection_for_anim",animation);
undo_redo->commit_action();
- selection.clear();
+ //selection.clear();
accept_event();
+ _edit_if_single_selection();
}
} else if (animation.is_valid() && animation->get_track_count()>0) {
@@ -1375,7 +1729,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
if (p_input.is_action("ui_up"))
selected_track--;
if (v_scroll->is_visible() && p_input.is_action("ui_page_up"))
- selected_track=selected_track--;;
+ selected_track--;
if (selected_track<0)
selected_track=0;
@@ -1552,20 +1906,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
}
- if (mb.mod.command || edit_button->is_pressed()) {
-
- key_edit->animation=animation;
- key_edit->track=idx;
- key_edit->key_ofs=animation->track_get_key_time(idx,key);
- key_edit->hint=_find_hint_for_track(idx);
- key_edit->notify_change();
-
- key_edit_dialog->set_size( Size2( 200,200) );
- key_edit_dialog->set_pos( track_editor->get_global_pos() + ofs + mpos +Point2(-100,20));
- key_edit_dialog->popup();
-
- }
SelectedKey sk;
sk.track=idx;
@@ -1577,7 +1918,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
if (!mb.mod.shift && !selection.has(sk))
- selection.clear();
+ _clear_selection();
selection.insert(sk,ki);
@@ -1588,7 +1929,10 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
selected_track=idx;
track_editor->update();
-
+ if (_edit_if_single_selection() && mb.mod.command) {
+ edit_button->set_pressed(true);
+ key_editor_tab->show();
+ }
} else {
//button column
int ofsx = size.width - mpos.x;
@@ -1824,7 +2168,8 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
if (from_track>to_track) {
if (!click.shift)
- selection.clear();
+ _clear_selection();
+ _edit_if_single_selection();
break;
}
@@ -1842,12 +2187,13 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
if (from_track > tracks_to || to_track < tracks_from) {
if (!click.shift)
- selection.clear();
+ _clear_selection();
+ _edit_if_single_selection();
break;
}
if (!click.shift)
- selection.clear();
+ _clear_selection();
int higher_track=0x7FFFFFFF;
@@ -1880,6 +2226,8 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
}
+ _edit_if_single_selection();
+
} break;
case ClickOver::CLICK_MOVE_KEYS: {
@@ -1891,8 +2239,9 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
if (!click.shift) {
KeyInfo ki=selection[click.selk];
- selection.clear();
+ _clear_selection();
selection[click.selk]=ki;
+ _edit_if_single_selection();
}
break;
@@ -2007,6 +2356,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
}
undo_redo->commit_action();
+ _edit_if_single_selection();
} break;
default: {}
@@ -2031,7 +2381,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
te->update();
track_editor->set_tooltip("");
- if (!track_editor->has_focus() && (!get_focus_owner() || !get_focus_owner()->cast_to<LineEdit>()))
+ if (!track_editor->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field()))
track_editor->call_deferred("grab_focus");
@@ -2321,12 +2671,14 @@ void AnimationKeyEditor::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
- zoomicon->set_texture( get_icon("Zoom","EditorIcons") );
- //menu_track->set_icon(get_icon("AddTrack","EditorIcons"));
- menu_track->get_popup()->add_icon_item(get_icon("KeyValue","EditorIcons"),"Add Normal Track",TRACK_MENU_ADD_VALUE_TRACK);
- menu_track->get_popup()->add_icon_item(get_icon("KeyXform","EditorIcons"),"Add Transform Track",TRACK_MENU_ADD_TRANSFORM_TRACK);
- menu_track->get_popup()->add_icon_item(get_icon("KeyCall","EditorIcons"),"Add Call Func Track",TRACK_MENU_ADD_CALL_TRACK);
- menu_track->get_popup()->add_separator();
+ zoomicon->set_texture( get_icon("Zoom","EditorIcons") );
+
+ menu_add_track->set_icon(get_icon("AddTrack","EditorIcons"));
+ menu_add_track->get_popup()->add_icon_item(get_icon("KeyValue","EditorIcons"),"Add Normal Track",ADD_TRACK_MENU_ADD_VALUE_TRACK);
+ menu_add_track->get_popup()->add_icon_item(get_icon("KeyXform","EditorIcons"),"Add Transform Track",ADD_TRACK_MENU_ADD_TRANSFORM_TRACK);
+ menu_add_track->get_popup()->add_icon_item(get_icon("KeyCall","EditorIcons"),"Add Call Func Track",ADD_TRACK_MENU_ADD_CALL_TRACK);
+
+ menu_track->set_icon(get_icon("Tools","EditorIcons"));
menu_track->get_popup()->add_item("Scale Selection",TRACK_MENU_SCALE);
menu_track->get_popup()->add_item("Scale From Cursor",TRACK_MENU_SCALE_PIVOT);
menu_track->get_popup()->add_separator();
@@ -2348,20 +2700,33 @@ void AnimationKeyEditor::_notification(int p_what) {
optimize_dialog->connect("confirmed",this,"_animation_optimize");
menu_track->get_popup()->add_child(tpp);
- menu_track->get_popup()->add_submenu_item("Set Transitions..","Transitions");
- menu_track->get_popup()->add_separator();
+ //menu_track->get_popup()->add_submenu_item("Set Transitions..","Transitions");
+ //menu_track->get_popup()->add_separator();
menu_track->get_popup()->add_item("Optimize Animation",TRACK_MENU_OPTIMIZE);
+ curve_linear->set_icon(get_icon("CurveLinear","EditorIcons"));
+ curve_in->set_icon(get_icon("CurveIn","EditorIcons"));
+ curve_out->set_icon(get_icon("CurveOut","EditorIcons"));
+ curve_inout->set_icon(get_icon("CurveInOut","EditorIcons"));
+ curve_outin->set_icon(get_icon("CurveOutIn","EditorIcons"));
+ curve_constant->set_icon(get_icon("CurveConstant","EditorIcons"));
-
+ curve_linear->connect("pressed",this,"_menu_track",varray(CURVE_SET_LINEAR));
+ curve_in->connect("pressed",this,"_menu_track",varray(CURVE_SET_IN));
+ curve_out->connect("pressed",this,"_menu_track",varray(CURVE_SET_OUT));
+ curve_inout->connect("pressed",this,"_menu_track",varray(CURVE_SET_INOUT));
+ curve_outin->connect("pressed",this,"_menu_track",varray(CURVE_SET_OUTIN));
+ curve_constant->connect("pressed",this,"_menu_track",varray(CURVE_SET_CONSTANT));
move_up_button->set_icon(get_icon("MoveUp","EditorIcons"));
move_down_button->set_icon(get_icon("MoveDown","EditorIcons"));
remove_button->set_icon(get_icon("Remove","EditorIcons"));
edit_button->set_icon(get_icon("EditKey","EditorIcons"));
+ edit_button->connect("pressed",this,"_toggle_edit_curves");
loop->set_icon(get_icon("Loop","EditorIcons"));
+ curve_edit->connect("transition_changed",this,"_curve_transition_changed");
//edit_button->add_color_override("font_color",get_color("font_color","Tree"));
//edit_button->add_color_override("font_color_hover",get_color("font_color","Tree"));
@@ -2390,6 +2755,7 @@ void AnimationKeyEditor::_notification(int p_what) {
}
+ call_select->connect("selected",this,"_add_call_track");
// rename_anim->set_icon( get_icon("Rename","EditorIcons") );
/*
edit_anim->set_icon( get_icon("Edit","EditorIcons") );
@@ -2456,6 +2822,17 @@ void AnimationKeyEditor::_update_menu() {
updating=false;
}
+void AnimationKeyEditor::_clear_selection() {
+
+ selection.clear();
+ key_edit->animation=Ref<Animation>();
+ key_edit->track=0;
+ key_edit->key_ofs=0;
+ key_edit->hint=PropertyInfo();
+ key_edit->notify_change();
+
+}
+
void AnimationKeyEditor::set_animation(const Ref<Animation>& p_anim) {
@@ -2466,11 +2843,12 @@ void AnimationKeyEditor::set_animation(const Ref<Animation>& p_anim) {
animation->connect("changed",this,"_update_paths");
timeline_pos=0;
- selection.clear();
+ _clear_selection();
_update_paths();
_update_menu();
selected_track=-1;
+ _edit_if_single_selection();
}
void AnimationKeyEditor::set_root(Node *p_root) {
@@ -2538,7 +2916,7 @@ void AnimationKeyEditor::_query_insert(const InsertData& p_id) {
insert_confirm->set_text("Create "+itos(insert_data.size())+" NEW tracks and insert keys?");
insert_confirm->get_ok()->set_text("Create");
- insert_confirm->popup_centered(Size2(300,100));
+ insert_confirm->popup_centered_minsize();
insert_query=true;
} else {
call_deferred("_insert_delay");
@@ -2591,6 +2969,7 @@ void AnimationKeyEditor::insert_transform_key(Spatial *p_node,const String& p_su
id.value=p_xform;
id.type=Animation::TYPE_TRANSFORM;
id.query="node '"+p_node->get_name()+"'";
+ id.advance=false;
//dialog insert
@@ -2643,6 +3022,7 @@ void AnimationKeyEditor::insert_node_value_key(Node* p_node, const String& p_pro
id.value=p_value;
id.type=Animation::TYPE_VALUE;
id.query="property '"+p_property+"'";
+ id.advance=false;
//dialog insert
_query_insert(id);
@@ -2650,7 +3030,7 @@ void AnimationKeyEditor::insert_node_value_key(Node* p_node, const String& p_pro
}
-void AnimationKeyEditor::insert_value_key(const String& p_property,const Variant& p_value) {
+void AnimationKeyEditor::insert_value_key(const String& p_property,const Variant& p_value,bool p_advance) {
ERR_FAIL_COND(!root);
//let's build a node path
@@ -2696,6 +3076,7 @@ void AnimationKeyEditor::insert_value_key(const String& p_property,const Variant
id.value=p_value;
id.type=Animation::TYPE_VALUE;
id.query="property '"+p_property+"'";
+ id.advance=p_advance;
//dialog insert
_query_insert(id);
@@ -2909,11 +3290,11 @@ void AnimationKeyEditor::set_anim_pos(float p_pos) {
void AnimationKeyEditor::_pane_drag(const Point2& p_delta) {
- Size2 ecs = ec->get_minsize();
+ Size2 ecs = ec->get_custom_minimum_size();
ecs.y-=p_delta.y;
if (ecs.y<100)
ecs.y=100;
- ec->set_minsize(ecs);;
+ ec->set_custom_minimum_size(ecs);;
}
@@ -2928,20 +3309,39 @@ void AnimationKeyEditor::_insert_delay() {
undo_redo->create_action("Anim Insert");
int last_track = animation->get_track_count();
+ bool advance=false;
while(insert_data.size()) {
+ if (insert_data.front()->get().advance)
+ advance=true;
last_track=_confirm_insert(insert_data.front()->get(),last_track);
insert_data.pop_front();
}
undo_redo->commit_action();
+
+ if (advance) {
+ float step = animation->get_step();
+ if (step==0)
+ step=1;
+
+ float pos=timeline_pos;
+
+ pos=Math::stepify(pos+step,step);
+ if (pos>animation->get_length())
+ pos=animation->get_length();
+ timeline_pos=pos;
+ track_pos->update();
+ emit_signal("timeline_changed",pos);
+ }
insert_queue=false;
}
void AnimationKeyEditor::_step_changed(float p_len) {
updating=true;
- animation->set_step(p_len);
+ if (!animation.is_null())
+ animation->set_step(p_len);
updating=false;
}
@@ -3077,6 +3477,26 @@ void AnimationKeyEditor::_scale() {
}
+void AnimationKeyEditor::_add_call_track(const NodePath& p_base) {
+
+ print_line("BASE IS "+String(p_base));
+ Node* base = EditorNode::get_singleton()->get_edited_scene();
+ if (!base)
+ return;
+ Node* from=base->get_node(p_base);
+ if (!from || !root)
+ return;
+
+ NodePath path = root->get_path_to(from);
+
+ undo_redo->create_action("Anim Add Call Track");
+ undo_redo->add_do_method(animation.ptr(),"add_track",Animation::TYPE_METHOD);
+ undo_redo->add_do_method(animation.ptr(),"track_set_path",animation->get_track_count(),path);
+ undo_redo->add_undo_method(animation.ptr(),"remove_track",animation->get_track_count());
+ undo_redo->commit_action();
+
+}
+
void AnimationKeyEditor::cleanup() {
set_animation(Ref<Animation>());
@@ -3105,6 +3525,7 @@ void AnimationKeyEditor::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_track_editor_input_event"),&AnimationKeyEditor::_track_editor_input_event);
ObjectTypeDB::bind_method(_MD("_track_name_changed"),&AnimationKeyEditor::_track_name_changed);
ObjectTypeDB::bind_method(_MD("_track_menu_selected"),&AnimationKeyEditor::_track_menu_selected);
+ ObjectTypeDB::bind_method(_MD("_menu_add_track"),&AnimationKeyEditor::_menu_add_track);
ObjectTypeDB::bind_method(_MD("_menu_track"),&AnimationKeyEditor::_menu_track);
ObjectTypeDB::bind_method(_MD("_clear_selection_for_anim"),&AnimationKeyEditor::_clear_selection_for_anim);
ObjectTypeDB::bind_method(_MD("_select_at_anim"),&AnimationKeyEditor::_select_at_anim);
@@ -3122,12 +3543,16 @@ void AnimationKeyEditor::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_animation"),&AnimationKeyEditor::set_animation);
ObjectTypeDB::bind_method(_MD("_animation_optimize"),&AnimationKeyEditor::_animation_optimize);
+ ObjectTypeDB::bind_method(_MD("_curve_transition_changed"),&AnimationKeyEditor::_curve_transition_changed);
+ ObjectTypeDB::bind_method(_MD("_toggle_edit_curves"),&AnimationKeyEditor::_toggle_edit_curves);
+ ObjectTypeDB::bind_method(_MD("_add_call_track"),&AnimationKeyEditor::_add_call_track);
ADD_SIGNAL( MethodInfo("resource_selected", PropertyInfo( Variant::OBJECT, "res"),PropertyInfo( Variant::STRING, "prop") ) );
ADD_SIGNAL( MethodInfo("keying_changed" ) );
ADD_SIGNAL( MethodInfo("timeline_changed", PropertyInfo(Variant::REAL,"pos") ) );
ADD_SIGNAL( MethodInfo("animation_len_changed", PropertyInfo(Variant::REAL,"len") ) );
+ ADD_SIGNAL( MethodInfo("key_edited", PropertyInfo(Variant::INT,"track"), PropertyInfo(Variant::INT,"key") ) );
}
@@ -3153,15 +3578,6 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
//menu->set_pos(Point2());
//add_child(menu);
- menu_track = memnew( MenuButton );
- menu_track->set_text("Tracks");
- hb->add_child(menu_track);
- menu_track->get_popup()->connect("item_pressed",this,"_menu_track");
-
-
-
- hb->add_child( memnew( VSeparator ) );
-
zoomicon = memnew( TextureFrame );
hb->add_child(zoomicon);
zoomicon->set_tooltip("Animation zoom.");
@@ -3219,12 +3635,10 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
hb->add_child( memnew( VSeparator ) );
- edit_button = memnew( ToolButton );
- edit_button->set_toggle_mode(true);
- edit_button->set_focus_mode(FOCUS_NONE);
- edit_button->set_disabled(true);
- hb->add_child(edit_button);
- edit_button->set_tooltip("Enable editing of individual keys by clicking them.");
+ menu_add_track = memnew( MenuButton );
+ hb->add_child(menu_add_track);
+ menu_add_track->get_popup()->connect("item_pressed",this,"_menu_add_track");
+ menu_add_track->set_tooltip("Add new tracks.");
move_up_button = memnew( ToolButton );
hb->add_child(move_up_button);
@@ -3238,7 +3652,7 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
move_down_button->connect("pressed",this,"_menu_track",make_binds(TRACK_MENU_MOVE_DOWN));
move_down_button->set_focus_mode(FOCUS_NONE);
move_down_button->set_disabled(true);
- move_down_button->set_tooltip("Move current track dosn.");
+ move_down_button->set_tooltip("Move current track down.");
remove_button = memnew( ToolButton );
hb->add_child(remove_button);
@@ -3247,6 +3661,20 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
remove_button->set_disabled(true);
remove_button->set_tooltip("Remove selected track.");
+ hb->add_child(memnew( VSeparator ));
+
+ menu_track = memnew( MenuButton );
+ hb->add_child(menu_track);
+ menu_track->get_popup()->connect("item_pressed",this,"_menu_track");
+ menu_track->set_tooltip("Track tools");
+
+ edit_button = memnew( ToolButton );
+ edit_button->set_toggle_mode(true);
+ edit_button->set_focus_mode(FOCUS_NONE);
+ edit_button->set_disabled(true);
+
+ hb->add_child(edit_button);
+ edit_button->set_tooltip("Enable editing of individual keys by clicking them.");
optimize_dialog = memnew( ConfirmationDialog );
add_child(optimize_dialog);
@@ -3289,15 +3717,15 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
keying->connect("pressed",this,"_keying_toggled");
*/
- l = memnew( Label );
+/* l = memnew( Label );
l->set_text("Base: ");
l->set_pos(Point2(0,3));
-// dr_panel->add_child(l);
+// dr_panel->add_child(l);*/
// menu->get_popup()->connect("item_pressed",this,"_menu_callback");
- ec = memnew (EmptyControl);
- ec->set_minsize(Size2(0,50));
+ ec = memnew (Control);
+ ec->set_custom_minimum_size(Size2(0,150));
add_child(ec);
ec->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -3313,6 +3741,7 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
track_editor->set_focus_mode(Control::FOCUS_ALL);
track_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+
track_pos = memnew( Control );
track_pos->set_area_as_parent_rect();
track_pos->set_ignore_mouse(true);
@@ -3325,6 +3754,56 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
v_scroll->connect("value_changed",this,"_scroll_changed");
v_scroll->set_val(0);
+ key_editor_tab = memnew(TabContainer);
+ hb->add_child(key_editor_tab);
+ key_editor_tab->set_custom_minimum_size(Size2(200,0));
+
+ key_editor = memnew( PropertyEditor );
+ key_editor->set_area_as_parent_rect();
+ key_editor->hide_top_label();
+ key_editor->set_name("Key");
+ key_editor_tab->add_child(key_editor);
+
+ key_edit = memnew( AnimationKeyEdit );
+ key_edit->undo_redo=undo_redo;
+ //key_edit->ke_dialog=key_edit_dialog;
+ key_editor->edit(key_edit);
+ type_menu = memnew( PopupMenu );
+ add_child(type_menu);
+ for(int i=0;i<Variant::VARIANT_MAX;i++)
+ type_menu->add_item(Variant::get_type_name(Variant::Type(i)),i);
+ type_menu->connect("item_pressed",this,"_create_value_item");
+
+ VBoxContainer *curve_vb = memnew( VBoxContainer );
+ curve_vb->set_name("Transition");
+ HBoxContainer *curve_hb = memnew( HBoxContainer );
+ curve_vb->add_child(curve_hb);
+
+ curve_linear = memnew( ToolButton );
+ curve_linear->set_focus_mode(FOCUS_NONE);
+ curve_hb->add_child(curve_linear);
+ curve_in = memnew( ToolButton );
+ curve_in->set_focus_mode(FOCUS_NONE);
+ curve_hb->add_child(curve_in);
+ curve_out = memnew( ToolButton );
+ curve_out->set_focus_mode(FOCUS_NONE);
+ curve_hb->add_child(curve_out);
+ curve_inout = memnew( ToolButton );
+ curve_inout->set_focus_mode(FOCUS_NONE);
+ curve_hb->add_child(curve_inout);
+ curve_outin = memnew( ToolButton );
+ curve_outin->set_focus_mode(FOCUS_NONE);
+ curve_hb->add_child(curve_outin);
+ curve_constant = memnew( ToolButton );
+ curve_constant->set_focus_mode(FOCUS_NONE);
+ curve_hb->add_child(curve_constant);
+
+
+ curve_edit = memnew( AnimationCurveEdit );
+ curve_vb->add_child(curve_edit);
+ curve_edit->set_v_size_flags(SIZE_EXPAND_FILL);
+ key_editor_tab->add_child(curve_vb);
+
h_scroll = memnew( HScrollBar );
h_scroll->connect("value_changed",this,"_scroll_changed");
add_child(h_scroll);
@@ -3340,7 +3819,7 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
add_child(track_menu);
track_menu->connect("item_pressed",this,"_track_menu_selected");
-
+ key_editor_tab->hide();
last_idx =1;
@@ -3359,24 +3838,6 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
timeline_pos=0;
- key_edit_dialog = memnew( PopupDialog );
- key_editor = memnew( PropertyEditor );
- add_child(key_edit_dialog);
- key_editor->set_area_as_parent_rect();
- key_editor->hide_top_label();
- for(int i=0;i<4;i++)
- key_editor->set_margin(Margin(i),5);
- key_edit_dialog->add_child(key_editor);
-
- key_edit = memnew( AnimationKeyEdit );
- key_edit->undo_redo=undo_redo;
- key_edit->ke_dialog=key_edit_dialog;
- key_editor->edit(key_edit);
- type_menu = memnew( PopupMenu );
- add_child(type_menu);
- for(int i=0;i<Variant::VARIANT_MAX;i++)
- type_menu->add_item(Variant::get_type_name(Variant::Type(i)),i);
- type_menu->connect("item_pressed",this,"_create_value_item");
keying=false;
insert_frame=0;
insert_query=false;
@@ -3397,7 +3858,9 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
scale_dialog->connect("confirmed",this,"_scale");
add_child(scale_dialog);
-
+ call_select = memnew( SceneTreeDialog );
+ add_child(call_select);
+ call_select->set_title("Call Functions in Which Node?");
}
diff --git a/tools/editor/animation_editor.h b/tools/editor/animation_editor.h
index 9d3e692fad..629377d78e 100644
--- a/tools/editor/animation_editor.h
+++ b/tools/editor/animation_editor.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,15 +37,17 @@
#include "scene/gui/scroll_bar.h"
#include "scene/gui/tool_button.h"
#include "scene/gui/file_dialog.h"
-#include "scene/gui/empty_control.h"
+#include "scene/gui/tab_container.h"
+
#include "scene/resources/animation.h"
#include "scene/animation/animation_cache.h"
#include "scene_tree_editor.h"
#include "editor_data.h"
#include "property_editor.h"
-
+#include "scene_tree_editor.h"
class AnimationKeyEdit;
+class AnimationCurveEdit;
class AnimationKeyEditor : public VBoxContainer {
@@ -68,9 +70,9 @@ class AnimationKeyEditor : public VBoxContainer {
enum {
- TRACK_MENU_ADD_VALUE_TRACK,
- TRACK_MENU_ADD_TRANSFORM_TRACK,
- TRACK_MENU_ADD_CALL_TRACK,
+ ADD_TRACK_MENU_ADD_VALUE_TRACK,
+ ADD_TRACK_MENU_ADD_TRANSFORM_TRACK,
+ ADD_TRACK_MENU_ADD_CALL_TRACK,
TRACK_MENU_SCALE,
TRACK_MENU_SCALE_PIVOT,
TRACK_MENU_MOVE_UP,
@@ -86,7 +88,13 @@ class AnimationKeyEditor : public VBoxContainer {
TRACK_MENU_SET_ALL_TRANS_OUTIN,
TRACK_MENU_NEXT_STEP,
TRACK_MENU_PREV_STEP,
- TRACK_MENU_OPTIMIZE
+ TRACK_MENU_OPTIMIZE,
+ CURVE_SET_LINEAR,
+ CURVE_SET_IN,
+ CURVE_SET_OUT,
+ CURVE_SET_INOUT,
+ CURVE_SET_OUTIN,
+ CURVE_SET_CONSTANT
};
struct MouseOver {
@@ -157,7 +165,7 @@ class AnimationKeyEditor : public VBoxContainer {
PopupMenu *track_menu;
PopupMenu *type_menu;
- EmptyControl *ec;
+ Control *ec;
TextureFrame *zoomicon;
HSlider *zoom;
//MenuButton *menu;
@@ -169,6 +177,14 @@ class AnimationKeyEditor : public VBoxContainer {
ToolButton *move_down_button;
ToolButton *remove_button;
+ ToolButton *curve_linear;
+ ToolButton *curve_in;
+ ToolButton *curve_out;
+ ToolButton *curve_inout;
+ ToolButton *curve_outin;
+ ToolButton *curve_constant;
+
+
ConfirmationDialog *optimize_dialog;
SpinBox *optimize_linear_error;
SpinBox *optimize_angular_error;
@@ -176,6 +192,7 @@ class AnimationKeyEditor : public VBoxContainer {
SpinBox *step;
+ MenuButton *menu_add_track;
MenuButton *menu_track;
HScrollBar *h_scroll;
@@ -183,13 +200,15 @@ class AnimationKeyEditor : public VBoxContainer {
Control *track_editor;
Control *track_pos;
+ TabContainer *key_editor_tab;
ConfirmationDialog *scale_dialog;
SpinBox *scale;
- PopupDialog *key_edit_dialog;
PropertyEditor *key_editor;
+ SceneTreeDialog *call_select;
+
Ref<Animation> animation;
void _update_paths();
@@ -203,6 +222,7 @@ class AnimationKeyEditor : public VBoxContainer {
AnimationKeyEdit *key_edit;
+ AnimationCurveEdit *curve_edit;
bool inserting;
@@ -220,6 +240,7 @@ class AnimationKeyEditor : public VBoxContainer {
int track_idx;
Variant value;
String query;
+ bool advance;
};/* insert_data;*/
bool insert_query;
@@ -254,7 +275,7 @@ class AnimationKeyEditor : public VBoxContainer {
void _scale();
-
+ void _clear_selection();
//void _browse_path();
@@ -266,18 +287,24 @@ class AnimationKeyEditor : public VBoxContainer {
void _scroll_changed(double);
+ void _menu_add_track(int p_type);
void _menu_track(int p_type);
void _clear_selection_for_anim(const Ref<Animation>& p_anim);
void _select_at_anim(const Ref<Animation>& p_anim,int p_track,float p_pos);
+ void _curve_transition_changed(float p_what);
PropertyInfo _find_hint_for_track(int p_idx);
void _create_value_item(int p_type);
void _pane_drag(const Point2& p_delta);
+ bool _edit_if_single_selection();
+ void _toggle_edit_curves();
void _animation_len_update();
+ void _add_call_track(const NodePath& p_base);
+
void _root_removed();
protected:
@@ -296,7 +323,7 @@ public:
void set_anim_pos(float p_pos);
void insert_node_value_key(Node* p_node, const String& p_property,const Variant& p_value,bool p_only_if_exists=false);
- void insert_value_key(const String& p_property,const Variant& p_value);
+ void insert_value_key(const String& p_property, const Variant& p_value, bool p_advance);
void insert_transform_key(Spatial *p_node,const String& p_sub,const Transform& p_xform);
AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_history, EditorSelection *p_selection);
diff --git a/tools/editor/array_property_edit.cpp b/tools/editor/array_property_edit.cpp
new file mode 100644
index 0000000000..9cd443270b
--- /dev/null
+++ b/tools/editor/array_property_edit.cpp
@@ -0,0 +1,231 @@
+#include "array_property_edit.h"
+
+#include "editor_node.h"
+
+#define ITEMS_PER_PAGE 100
+
+Variant ArrayPropertyEdit::get_array() const{
+
+ Object*o = ObjectDB::get_instance(obj);
+ if (!o)
+ return Array();
+ Variant arr=o->get(property);
+ if (!arr.is_array()) {
+ Variant::CallError ce;
+ arr=Variant::construct(default_type,NULL,0,ce);
+ }
+ return arr;
+}
+
+void ArrayPropertyEdit::_notif_change() {
+ _change_notify();
+}
+void ArrayPropertyEdit::_notif_changev(const String& p_v) {
+
+ _change_notify(p_v.utf8().get_data());
+}
+
+void ArrayPropertyEdit::_set_size(int p_size) {
+
+ Variant arr = get_array();
+ arr.call("resize",p_size);
+ Object*o = ObjectDB::get_instance(obj);
+ if (!o)
+ return;
+
+ o->set(property,arr);
+
+}
+
+void ArrayPropertyEdit::_set_value(int p_idx,const Variant& p_value) {
+
+ Variant arr = get_array();
+ arr.set(p_idx,p_value);
+ Object*o = ObjectDB::get_instance(obj);
+ if (!o)
+ return;
+
+ o->set(property,arr);
+}
+
+bool ArrayPropertyEdit::_set(const StringName& p_name, const Variant& p_value){
+
+ String pn=p_name;
+
+ if (pn.begins_with("array/")) {
+
+ if (pn=="array/size") {
+
+ Variant arr = get_array();
+ int size = arr.call("size");
+
+ int newsize=p_value;
+ if (newsize==size)
+ return true;
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Resize Array");
+ ur->add_do_method(this,"_set_size",newsize);
+ ur->add_undo_method(this,"_set_size",size);
+ if (newsize<size) {
+ for(int i=newsize;i<size;i++) {
+ ur->add_undo_method(this,"_set_value",i,arr.get(i));
+
+ }
+ }
+ ur->add_do_method(this,"_notif_change");
+ ur->add_undo_method(this,"_notif_change");
+ ur->commit_action();
+ return true;
+ }
+ if (pn=="array/page") {
+ page=p_value;
+ _change_notify();
+ return true;
+ }
+ } else if (pn.begins_with("indices")) {
+
+ if (pn.find("_")!=-1) {
+ //type
+ int idx=pn.get_slicec('/',1).get_slicec('_',0).to_int();
+
+ int type = p_value;
+
+ Variant arr = get_array();
+
+ Variant value = arr.get(idx);
+ if (value.get_type()!=type && type>=0 && type<Variant::VARIANT_MAX) {
+ Variant::CallError ce;
+ Variant new_value=Variant::construct(Variant::Type(type),NULL,0,ce);
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+
+ ur->create_action("Change Array Value Type");
+ ur->add_do_method(this,"_set_value",idx,new_value);
+ ur->add_undo_method(this,"_set_value",idx,value);
+ ur->add_do_method(this,"_notif_change");
+ ur->add_undo_method(this,"_notif_change");
+ ur->commit_action();
+
+ }
+ return true;
+
+ } else {
+ int idx=pn.get_slicec('/',1).to_int();
+ Variant arr = get_array();
+
+ Variant value = arr.get(idx);
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+
+ ur->create_action("Change Array Value");
+ ur->add_do_method(this,"_set_value",idx,p_value);
+ ur->add_undo_method(this,"_set_value",idx,value);
+ ur->add_do_method(this,"_notif_changev",p_name);
+ ur->add_undo_method(this,"_notif_changev",p_name);
+ ur->commit_action();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ArrayPropertyEdit::_get(const StringName& p_name,Variant &r_ret) const {
+
+ Variant arr = get_array();
+ //int size = arr.call("size");
+
+ String pn=p_name;
+ if (pn.begins_with("array/")) {
+
+ if (pn=="array/size") {
+ r_ret=arr.call("size");
+ return true;
+ }
+ if (pn=="array/page") {
+ r_ret=page;
+ return true;
+ }
+ } else if (pn.begins_with("indices")) {
+
+ if (pn.find("_")!=-1) {
+ //type
+ int idx=pn.get_slicec('/',1).get_slicec('_',0).to_int();
+ bool valid;
+ r_ret=arr.get(idx,&valid);
+ if (valid)
+ r_ret=r_ret.get_type();
+ return valid;
+
+ } else {
+ int idx=pn.get_slicec('/',1).to_int();
+ bool valid;
+ r_ret=arr.get(idx,&valid);
+ return valid;
+ }
+ }
+
+ return false;
+}
+
+void ArrayPropertyEdit::_get_property_list( List<PropertyInfo> *p_list) const{
+
+ Variant arr = get_array();
+ int size = arr.call("size");
+
+ p_list->push_back( PropertyInfo(Variant::INT,"array/size",PROPERTY_HINT_RANGE,"0,100000,1") );
+ int pages = size/ITEMS_PER_PAGE;
+ if (pages>0)
+ p_list->push_back( PropertyInfo(Variant::INT,"array/page",PROPERTY_HINT_RANGE,"0,"+itos(pages)+",1") );
+
+ int offset=page*ITEMS_PER_PAGE;
+
+ int items=MIN(size-offset,ITEMS_PER_PAGE);
+
+
+ for(int i=0;i<items;i++) {
+
+ Variant v=arr.get(i+offset);
+ if (arr.get_type()==Variant::ARRAY) {
+ p_list->push_back(PropertyInfo(Variant::INT,"indices/"+itos(i+offset)+"_type",PROPERTY_HINT_ENUM,vtypes));
+ }
+ if (arr.get_type()!=Variant::ARRAY || v.get_type()!=Variant::NIL) {
+ PropertyInfo pi(v.get_type(),"indices/"+itos(i+offset));
+ if (v.get_type()==Variant::OBJECT) {
+ pi.hint=PROPERTY_HINT_RESOURCE_TYPE;
+ pi.hint_string="Resource";
+ }
+ p_list->push_back(pi);
+ }
+ }
+
+}
+
+void ArrayPropertyEdit::edit(Object* p_obj,const StringName& p_prop,Variant::Type p_deftype) {
+
+ page=0;
+ property=p_prop;
+ obj=p_obj->get_instance_ID();
+ default_type=p_deftype;
+
+}
+
+void ArrayPropertyEdit::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_set_size"),&ArrayPropertyEdit::_set_size);
+ ObjectTypeDB::bind_method(_MD("_set_value"),&ArrayPropertyEdit::_set_value);
+ ObjectTypeDB::bind_method(_MD("_notif_change"),&ArrayPropertyEdit::_notif_change);
+ ObjectTypeDB::bind_method(_MD("_notif_changev"),&ArrayPropertyEdit::_notif_changev);
+}
+
+ArrayPropertyEdit::ArrayPropertyEdit()
+{
+ page=0;
+ for(int i=0;i<Variant::VARIANT_MAX;i++) {
+
+ if (i>0)
+ vtypes+=",";
+ vtypes+=Variant::get_type_name( Variant::Type(i) );
+ }
+ default_type=Variant::NIL;
+
+}
diff --git a/tools/editor/array_property_edit.h b/tools/editor/array_property_edit.h
new file mode 100644
index 0000000000..acfb8e68ed
--- /dev/null
+++ b/tools/editor/array_property_edit.h
@@ -0,0 +1,36 @@
+#ifndef ARRAY_PROPERTY_EDIT_H
+#define ARRAY_PROPERTY_EDIT_H
+
+#include "scene/main/node.h"
+
+class ArrayPropertyEdit : public Reference {
+
+ OBJ_TYPE(ArrayPropertyEdit,Reference);
+
+ int page;
+ ObjectID obj;
+ StringName property;
+ String vtypes;
+ Variant get_array() const;
+ Variant::Type default_type;
+
+ void _notif_change();
+ void _notif_changev(const String& p_v);
+ void _set_size(int p_size);
+ void _set_value(int p_idx,const Variant& p_value);
+
+protected:
+
+ static void _bind_methods();
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+
+public:
+
+ void edit(Object* p_obj, const StringName& p_prop, Variant::Type p_deftype);
+
+ ArrayPropertyEdit();
+};
+
+#endif // ARRAY_PROPERTY_EDIT_H
diff --git a/tools/editor/call_dialog.cpp b/tools/editor/call_dialog.cpp
index 2e4fb96a58..0e3abcf4ef 100644
--- a/tools/editor/call_dialog.cpp
+++ b/tools/editor/call_dialog.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/call_dialog.h b/tools/editor/call_dialog.h
index b7bf1affda..fe69847796 100644
--- a/tools/editor/call_dialog.h
+++ b/tools/editor/call_dialog.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/code_editor.cpp b/tools/editor/code_editor.cpp
index 242654c472..685763cadb 100644
--- a/tools/editor/code_editor.cpp
+++ b/tools/editor/code_editor.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -599,14 +599,26 @@ CodeTextEditor::CodeTextEditor() {
add_child(text_editor);
text_editor->set_area_as_parent_rect();
text_editor->set_margin(MARGIN_BOTTOM,20);
- text_editor->add_font_override("font",get_font("source","Fonts"));
+
+ String editor_font = EDITOR_DEF("text_editor/font", "");
+ bool font_overrode = false;
+ if (editor_font!="") {
+ Ref<Font> fnt = ResourceLoader::load(editor_font);
+ if (fnt.is_valid()) {
+ text_editor->add_font_override("font",fnt);
+ font_overrode = true;
+ }
+ }
+
+ if (!font_overrode)
+ text_editor->add_font_override("font",get_font("source","Fonts"));
text_editor->set_show_line_numbers(true);
text_editor->set_brace_matching(true);
line_col = memnew( Label );
add_child(line_col);
line_col->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,135);
- line_col->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,20);
+ line_col->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,15);
line_col->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,1);
line_col->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,5);
//line_col->set_align(Label::ALIGN_RIGHT);
@@ -625,7 +637,7 @@ CodeTextEditor::CodeTextEditor() {
error = memnew( Label );
add_child(error);
error->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN,5);
- error->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,20);
+ error->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,15);
error->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,1);
error->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,130);
error->hide();
diff --git a/tools/editor/code_editor.h b/tools/editor/code_editor.h
index f82eaf5ec5..0c32aeb68f 100644
--- a/tools/editor/code_editor.h
+++ b/tools/editor/code_editor.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/connections_dialog.cpp b/tools/editor/connections_dialog.cpp
index d7158eb7e9..b0bacdae61 100644
--- a/tools/editor/connections_dialog.cpp
+++ b/tools/editor/connections_dialog.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -172,7 +172,7 @@ void ConnectDialog::ok_pressed() {
if (dst_method->get_text()=="") {
error->set_text("Method in target Node must be specified!");
- error->popup_centered(Size2(300,80));
+ error->popup_centered_minsize();
return;
}
emit_signal("connected");
@@ -530,7 +530,7 @@ void ConnectionsDialog::ok_pressed() {
get_ok()->set_disabled(true);
return;
}
- if (item->get_parent()==tree->get_root()) {
+ if (item->get_parent()==tree->get_root() || item->get_parent()->get_parent()==tree->get_root()) {
//a signal - connect
String signal=item->get_metadata(0).operator Dictionary()["name"];
String signalname=signal;
@@ -607,6 +607,14 @@ void ConnectionsDialog::_remove_confirm() {
}
*/
+
+struct _ConnectionsDialogMethodInfoSort {
+
+ _FORCE_INLINE_ bool operator()(const MethodInfo& a, const MethodInfo& b) const {
+ return a.name < b.name;
+ }
+};
+
void ConnectionsDialog::update_tree() {
if (!is_visible())
@@ -623,74 +631,131 @@ void ConnectionsDialog::update_tree() {
node->get_signal_list(&node_signals);
-
- for(List<MethodInfo>::Element *E=node_signals.front();E;E=E->next()) {
-
+ //node_signals.sort_custom<_ConnectionsDialogMethodInfoSort>();
+ bool did_script=false;
+ StringName base = node->get_type();
- MethodInfo &mi =E->get();
+ while(base) {
- String signaldesc;
- signaldesc=mi.name+"(";
- StringArray argnames;
- if (mi.arguments.size()) {
- signaldesc+=" ";
- for(int i=0;i<mi.arguments.size();i++) {
+ List<MethodInfo> node_signals;
+ Ref<Texture> icon;
+ String name;
- PropertyInfo &pi = mi.arguments[i];
+ if (!did_script) {
- if (i>0)
- signaldesc+=", ";
- signaldesc+=Variant::get_type_name(pi.type)+" "+(pi.name==""?String("arg "+itos(i)):pi.name);
- argnames.push_back(pi.name);
+ Ref<Script> scr = node->get_script();
+ if (scr.is_valid()) {
+ scr->get_script_signal_list(&node_signals);
+ if (scr->get_path().is_resource_file())
+ name=scr->get_path().get_file();
+ else
+ name=scr->get_type();
+ if (has_icon(scr->get_type(),"EditorIcons")) {
+ icon=get_icon(scr->get_type(),"EditorIcons");
+ }
}
- signaldesc+=" ";
+
+ } else {
+
+ ObjectTypeDB::get_signal_list(base,&node_signals,true);
+ if (has_icon(base,"EditorIcons")) {
+ icon=get_icon(base,"EditorIcons");
+ }
+ name=base;
}
- signaldesc+=")";
-
- TreeItem *item=tree->create_item(root);
- item->set_text(0,signaldesc);
- Dictionary sinfo;
- sinfo["name"]=mi.name;
- sinfo["args"]=argnames;
- item->set_metadata(0,sinfo);
- item->set_icon(0,get_icon("Signal","EditorIcons"));
- List<Object::Connection> connections;
- node->get_signal_connection_list(mi.name,&connections);
+ TreeItem *pitem = NULL;
- for(List<Object::Connection>::Element *F=connections.front();F;F=F->next()) {
+ if (node_signals.size()) {
+ pitem=tree->create_item(root);
+ pitem->set_text(0,name);
+ pitem->set_icon(0,icon);
+ pitem->set_selectable(0,false);
+ pitem->set_editable(0,false);
+ pitem->set_custom_bg_color(0,get_color("prop_subsection","Editor"));
+ node_signals.sort();
+ }
- Object::Connection&c = F->get();
- if (!(c.flags&CONNECT_PERSIST))
- continue;
+ for(List<MethodInfo>::Element *E=node_signals.front();E;E=E->next()) {
- Node *target = c.target->cast_to<Node>();
- if (!target)
- continue;
- String path = String(node->get_path_to(target))+" :: "+c.method+"()";
- if (c.flags&CONNECT_DEFERRED)
- path+=" (deferred)";
- if (c.binds.size()) {
+ MethodInfo &mi =E->get();
- path+=" binds( ";
- for(int i=0;i<c.binds.size();i++) {
+ String signaldesc;
+ signaldesc=mi.name+"(";
+ StringArray argnames;
+ if (mi.arguments.size()) {
+ signaldesc+=" ";
+ for(int i=0;i<mi.arguments.size();i++) {
+
+ PropertyInfo &pi = mi.arguments[i];
if (i>0)
- path+=", ";
- path+=c.binds[i].operator String();
+ signaldesc+=", ";
+ String tname="var";
+ if (pi.type!=Variant::NIL) {
+ tname=Variant::get_type_name(pi.type);
+ }
+ signaldesc+=tname+" "+(pi.name==""?String("arg "+itos(i)):pi.name);
+ argnames.push_back(pi.name);
+
}
- path+=" )";
+ signaldesc+=" ";
}
- TreeItem *item2=tree->create_item(item);
- item2->set_text(0,path);
- item2->set_metadata(0,c);
- item2->set_icon(0,get_icon("Slot","EditorIcons"));
+ signaldesc+=")";
+
+ TreeItem *item=tree->create_item(pitem);
+ item->set_text(0,signaldesc);
+ Dictionary sinfo;
+ sinfo["name"]=mi.name;
+ sinfo["args"]=argnames;
+ item->set_metadata(0,sinfo);
+ item->set_icon(0,get_icon("Signal","EditorIcons"));
+
+ List<Object::Connection> connections;
+ node->get_signal_connection_list(mi.name,&connections);
+
+ for(List<Object::Connection>::Element *F=connections.front();F;F=F->next()) {
+
+ Object::Connection&c = F->get();
+ if (!(c.flags&CONNECT_PERSIST))
+ continue;
+
+ Node *target = c.target->cast_to<Node>();
+ if (!target)
+ continue;
+
+ String path = String(node->get_path_to(target))+" :: "+c.method+"()";
+ if (c.flags&CONNECT_DEFERRED)
+ path+=" (deferred)";
+ if (c.binds.size()) {
+ path+=" binds( ";
+ for(int i=0;i<c.binds.size();i++) {
+
+ if (i>0)
+ path+=", ";
+ path+=c.binds[i].operator String();
+ }
+ path+=" )";
+ }
+
+ TreeItem *item2=tree->create_item(item);
+ item2->set_text(0,path);
+ item2->set_metadata(0,c);
+ item2->set_icon(0,get_icon("Slot","EditorIcons"));
+
+
+ }
+ }
+ if (!did_script) {
+ did_script=true;
+ } else {
+ base=ObjectTypeDB::type_inherits_from(base);
}
}
@@ -713,7 +778,7 @@ void ConnectionsDialog::_something_selected() {
get_ok()->set_text("Connect..");
get_ok()->set_disabled(true);
- } else if (item->get_parent()==tree->get_root()) {
+ } else if (item->get_parent()==tree->get_root() || item->get_parent()->get_parent()==tree->get_root()) {
//a signal - connect
get_ok()->set_text("Connect..");
get_ok()->set_disabled(false);
diff --git a/tools/editor/connections_dialog.h b/tools/editor/connections_dialog.h
index 1e223494c8..68b13bf07a 100644
--- a/tools/editor/connections_dialog.h
+++ b/tools/editor/connections_dialog.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/console.cpp b/tools/editor/console.cpp
index 6b37895bc4..0c98f05706 100644
--- a/tools/editor/console.cpp
+++ b/tools/editor/console.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/console.h b/tools/editor/console.h
index b4ff62fa75..aff425fcde 100644
--- a/tools/editor/console.h
+++ b/tools/editor/console.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/create_dialog.cpp b/tools/editor/create_dialog.cpp
index f816e46bcf..a9119349c8 100644
--- a/tools/editor/create_dialog.cpp
+++ b/tools/editor/create_dialog.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -128,7 +128,7 @@ void CreateDialog::_update_search() {
_parse_fs(EditorFileSystem::get_singleton()->get_filesystem());
*/
- List<String> type_list;
+ List<StringName> type_list;
ObjectTypeDB::get_type_list(&type_list);
HashMap<String,TreeItem*> types;
@@ -137,7 +137,7 @@ void CreateDialog::_update_search() {
root->set_text(0,base_type);
- List<String>::Element *I=type_list.front();
+ List<StringName>::Element *I=type_list.front();
TreeItem *to_select=NULL;
for(;I;I=I->next()) {
@@ -230,6 +230,11 @@ void CreateDialog::_notification(int p_what) {
connect("confirmed",this,"_confirmed");
_update_search();
}
+ if (p_what==NOTIFICATION_EXIT_TREE) {
+
+ disconnect("confirmed",this,"_confirmed");
+
+ }
if (p_what==NOTIFICATION_VISIBILITY_CHANGED) {
diff --git a/tools/editor/create_dialog.h b/tools/editor/create_dialog.h
index 35f9f2a1fb..f200e1caf5 100644
--- a/tools/editor/create_dialog.h
+++ b/tools/editor/create_dialog.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/default_saver.cpp b/tools/editor/default_saver.cpp
index bbeda8addb..c865adb1eb 100644
--- a/tools/editor/default_saver.cpp
+++ b/tools/editor/default_saver.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/default_saver.h b/tools/editor/default_saver.h
index 3c5d23b134..4e11ff5592 100644
--- a/tools/editor/default_saver.h
+++ b/tools/editor/default_saver.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/dependency_editor.cpp b/tools/editor/dependency_editor.cpp
new file mode 100644
index 0000000000..c04e82a08a
--- /dev/null
+++ b/tools/editor/dependency_editor.cpp
@@ -0,0 +1,512 @@
+#include "dependency_editor.h"
+#include "os/file_access.h"
+#include "scene/gui/margin_container.h"
+#include "io/resource_loader.h"
+#include "editor_node.h"
+
+void DependencyEditor::_notification(int p_what){
+
+
+}
+
+void DependencyEditor::_searched(const String& p_path) {
+
+ Map<String,String> dep_rename;
+ dep_rename[replacing]=p_path;
+
+
+ ResourceLoader::rename_dependencies(editing,dep_rename);
+
+ _update_list();
+ _update_file();
+}
+
+void DependencyEditor::_load_pressed(Object* p_item,int p_cell,int p_button){
+
+ TreeItem *ti=p_item->cast_to<TreeItem>();
+ String fname = ti->get_text(0);
+ replacing = ti->get_text(1);
+
+ search->set_title("Search Replacement For: "+replacing.get_file());
+
+ search->clear_filters();
+ List<String> ext;
+ ResourceLoader::get_recognized_extensions_for_type(ti->get_metadata(0),&ext);
+ for (List<String>::Element *E=ext.front();E;E=E->next()) {
+ search->add_filter("*"+E->get());
+ }
+ search->popup_centered_ratio();
+
+}
+
+void DependencyEditor::_fix_and_find(EditorFileSystemDirectory *efsd, Map<String,Map<String,String> >& candidates){
+
+ for(int i=0;i<efsd->get_subdir_count();i++) {
+ _fix_and_find(efsd->get_subdir(i),candidates);
+ }
+
+ for(int i=0;i<efsd->get_file_count();i++) {
+
+ String file = efsd->get_file(i);
+ if (!candidates.has(file))
+ continue;
+
+ String path = efsd->get_file_path(i);
+ Map<String,String> &ss = candidates[file];
+
+
+ for(Map<String,String>::Element *E=candidates[file].front();E;E=E->next()) {
+
+ if (E->get()==String()) {
+ E->get()=path;
+ continue;
+ }
+
+ //must match the best, using subdirs
+ String existing=E->get().replace_first("res://","");
+ String current=path.replace_first("res://","");
+ String lost=E->key().replace_first("res://","");
+
+ Vector<String> existingv=existing.split("/");
+ existingv.invert();
+ Vector<String> currentv=current.split("/");
+ currentv.invert();
+ Vector<String> lostv=lost.split("/");
+ lostv.invert();
+
+ int existing_score=0;
+ int current_score=0;
+
+ for(int j=0;j<lostv.size();j++) {
+
+ if (j<existingv.size() && lostv[j]==existingv[j]) {
+ existing_score++;
+ }
+ if (j<currentv.size() && lostv[j]==currentv[j]) {
+ current_score++;
+ }
+ }
+
+ if (current_score > existing_score) {
+
+ //if it was the same, could track distance to new path but..
+
+ E->get()=path; //replace by more accurate
+ }
+
+ }
+
+ }
+
+}
+
+
+void DependencyEditor::_fix_all(){
+
+ if (!EditorFileSystem::get_singleton()->get_filesystem())
+ return;
+
+ Map<String,Map<String,String> > candidates;
+
+ for (List<String>::Element *E=missing.front();E;E=E->next()) {
+
+ String base = E->get().get_file();
+ if (!candidates.has(base)) {
+ candidates[base]=Map<String,String>();
+ }
+
+ candidates[base][E->get()]="";
+ }
+
+ _fix_and_find(EditorFileSystem::get_singleton()->get_filesystem(),candidates);
+
+ Map<String,String> remaps;
+
+ for (Map<String,Map<String,String> >::Element *E=candidates.front();E;E=E->next()) {
+
+ for (Map<String,String>::Element *F=E->get().front();F;F=F->next()) {
+
+ if (F->get()!=String()) {
+ remaps[F->key()]=F->get();
+ }
+ }
+
+ }
+
+ if (remaps.size()) {
+
+ ResourceLoader::rename_dependencies(editing,remaps);
+
+ _update_list();
+ _update_file();
+ }
+}
+
+void DependencyEditor::_update_file() {
+
+ EditorFileSystem::get_singleton()->update_file(editing);
+
+}
+
+void DependencyEditor::_update_list() {
+
+ List<String> deps;
+ ResourceLoader::get_dependencies(editing,&deps,true);
+
+ tree->clear();
+ missing.clear();
+
+ TreeItem *root = tree->create_item();
+
+ Ref<Texture> folder = get_icon("folder","FileDialog");
+
+ bool broken=false;
+
+ for(List<String>::Element *E=deps.front();E;E=E->next()) {
+
+ TreeItem *item = tree->create_item(root);
+
+ String n = E->get();
+ String path;
+ String type;
+
+ if (n.find("::")!=-1) {
+ path = n.get_slice("::",0);
+ type = n.get_slice("::",1);
+ } else {
+ path=n;
+ type="Resource";
+ }
+ String name = path.get_file();
+
+ Ref<Texture> icon;
+ if (has_icon(type,"EditorIcons")) {
+ icon=get_icon(type,"EditorIcons");
+ } else {
+ icon=get_icon("Object","EditorIcons");
+ }
+ item->set_text(0,name);
+ item->set_icon(0,icon);
+ item->set_metadata(0,type);
+ item->set_text(1,path);
+
+ if (!FileAccess::exists(path)) {
+ item->set_custom_color(1,Color(1,0.4,0.3));
+ missing.push_back(path);
+ broken=true;
+ }
+
+ item->add_button(1,folder,0);
+ }
+
+ fixdeps->set_disabled(!broken);
+
+}
+
+
+
+void DependencyEditor::edit(const String& p_path) {
+
+
+ editing=p_path;
+ set_title("Dependencies For: "+p_path.get_file());
+
+ _update_list();
+ popup_centered_ratio();
+
+ if (EditorNode::get_singleton()->is_scene_open(p_path)) {
+ EditorNode::get_singleton()->show_warning("Scene '"+p_path.get_file()+"' is currently being edited.\nChanges will not take effect unless reloaded.");
+ } else if (ResourceCache::has(p_path)) {
+ EditorNode::get_singleton()->show_warning("Resource '"+p_path.get_file()+"' is in use.\nChanges will take effect when reloaded.");
+ }
+}
+
+
+void DependencyEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_searched"),&DependencyEditor::_searched);
+ ObjectTypeDB::bind_method(_MD("_load_pressed"),&DependencyEditor::_load_pressed);
+ ObjectTypeDB::bind_method(_MD("_fix_all"),&DependencyEditor::_fix_all);
+
+}
+
+DependencyEditor::DependencyEditor() {
+
+ VBoxContainer *vb = memnew( VBoxContainer );
+ vb->set_name("Dependencies");
+ add_child(vb);
+ set_child_rect(vb);
+
+ tree = memnew( Tree );
+ tree->set_columns(2);
+ tree->set_column_titles_visible(true);
+ tree->set_column_title(0,"Resource");
+ tree->set_column_title(1,"Path");
+ tree->set_hide_root(true);
+ tree->connect("button_pressed",this,"_load_pressed");
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ Label *label = memnew( Label("Dependencies:"));
+ hbc->add_child(label);
+ hbc->add_spacer();
+ fixdeps = memnew( Button("Fix Broken"));
+ hbc->add_child(fixdeps);
+ fixdeps->connect("pressed",this,"_fix_all");
+
+ vb->add_child(hbc);
+
+ MarginContainer *mc = memnew( MarginContainer );
+ mc->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ mc->add_child(tree);
+ vb->add_child(mc);
+
+ set_title("Dependency Editor");
+ search = memnew( EditorFileDialog );
+ search->connect("file_selected",this,"_searched");
+ search->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+ search->set_title("Search Replacement Resource:");
+ add_child(search);
+
+}
+
+/////////////////////////////////////
+
+
+
+void DependencyEditorOwners::_fill_owners(EditorFileSystemDirectory *efsd) {
+
+ if (!efsd)
+ return;
+
+ for(int i=0;i<efsd->get_subdir_count();i++) {
+ _fill_owners(efsd->get_subdir(i));
+ }
+
+ for(int i=0;i<efsd->get_file_count();i++) {
+
+ Vector<String> deps = efsd->get_file_deps(i);
+ //print_line(":::"+efsd->get_file_path(i));
+ bool found=false;
+ for(int j=0;j<deps.size();j++) {
+ //print_line("\t"+deps[j]+" vs "+editing);
+ if (deps[j]==editing) {
+ //print_line("found");
+ found=true;
+ break;
+ }
+ }
+ if (!found)
+ continue;
+
+ Ref<Texture> icon;
+ String type=efsd->get_file_type(i);
+ if (!has_icon(type,"EditorIcons")) {
+ icon=get_icon("Object","EditorIcons");
+ } else {
+ icon=get_icon(type,"EditorIcons");
+ }
+
+ owners->add_item(efsd->get_file_path(i),icon);
+ }
+
+}
+
+void DependencyEditorOwners::show(const String& p_path) {
+
+ editing=p_path;
+ owners->clear();
+ _fill_owners(EditorFileSystem::get_singleton()->get_filesystem());
+ popup_centered_ratio();
+
+ set_title("Owners Of: "+p_path.get_file());
+
+}
+
+DependencyEditorOwners::DependencyEditorOwners() {
+
+
+ owners = memnew( ItemList );
+ add_child(owners);
+ set_child_rect(owners);
+
+
+}
+
+///////////////////////
+
+
+void DependencyRemoveDialog::_fill_owners(EditorFileSystemDirectory *efsd) {
+
+ if (!efsd)
+ return;
+
+ for(int i=0;i<efsd->get_subdir_count();i++) {
+ _fill_owners(efsd->get_subdir(i));
+ }
+
+ for(int i=0;i<efsd->get_file_count();i++) {
+
+ Vector<String> deps = efsd->get_file_deps(i);
+ //print_line(":::"+efsd->get_file_path(i));
+ Set<String> met;
+ for(int j=0;j<deps.size();j++) {
+ if (files.has(deps[j])) {
+ met.insert(deps[j]);
+ }
+ }
+ if (!met.size())
+ continue;
+
+ exist=true;
+
+ Ref<Texture> icon;
+ String type=efsd->get_file_type(i);
+ if (!has_icon(type,"EditorIcons")) {
+ icon=get_icon("Object","EditorIcons");
+ } else {
+ icon=get_icon(type,"EditorIcons");
+ }
+
+
+ for(Set<String>::Element *E=met.front();E;E=E->next()) {
+
+ String which = E->get();
+ if (!files[which]) {
+ TreeItem *ti=owners->create_item(owners->get_root());
+ ti->set_text(0,which.get_file());
+ files[which]=ti;
+
+ }
+ TreeItem *ti=owners->create_item(files[which]);
+ ti->set_text(0,efsd->get_file_path(i));
+ ti->set_icon(0,icon);
+ }
+
+ }
+
+}
+
+void DependencyRemoveDialog::show(const Vector<String> &to_erase) {
+
+ exist=false;
+ owners->clear();
+ files.clear();
+ TreeItem *root=owners->create_item();
+ for(int i=0;i<to_erase.size();i++) {
+ files[to_erase[i]]=NULL;
+ }
+
+ _fill_owners(EditorFileSystem::get_singleton()->get_filesystem());
+
+ if (exist) {
+ owners->show();
+ text->set_text("The files being removed are required by other resources in order for them to work.\nRemove them anyway? (no undo)");
+ popup_centered_minsize(Size2(500,220));
+ } else {
+ owners->hide();
+ text->set_text("Remove selected files from the project? (no undo)");
+ popup_centered_minsize(Size2(400,100));
+ }
+
+}
+
+void DependencyRemoveDialog::ok_pressed() {
+
+
+ DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ for (Map<String,TreeItem*>::Element *E=files.front();E;E=E->next()) {
+
+ da->remove(E->key());
+ EditorFileSystem::get_singleton()->update_file(E->key());
+ }
+ memdelete(da);
+
+}
+
+DependencyRemoveDialog::DependencyRemoveDialog() {
+
+ VBoxContainer *vb = memnew( VBoxContainer );
+ add_child(vb);
+ set_child_rect(vb);
+
+ text = memnew( Label );
+ vb->add_child(text);
+
+ owners = memnew( Tree );
+ owners->set_hide_root(true);
+ vb->add_child(owners);
+ owners->set_v_size_flags(SIZE_EXPAND_FILL);
+ get_ok()->set_text("Remove");
+}
+
+
+//////////////
+
+
+void DependencyErrorDialog::show(const String& p_for_file,const Vector<String> &report) {
+
+
+ for_file=p_for_file;
+ set_title("Error loading: "+p_for_file.get_file());
+ files->clear();
+
+ TreeItem *root = files->create_item(NULL);
+ for(int i=0;i<report.size();i++) {
+
+ String dep;
+ String type="Object";
+ dep=report[i].get_slice("::",0);
+ if (report[i].get_slice_count("::")>0)
+ type=report[i].get_slice("::",1);
+
+ Ref<Texture> icon;
+ if (!has_icon(type,"EditorIcons")) {
+ icon=get_icon("Object","EditorIcons");
+ } else {
+ icon=get_icon(type,"EditorIcons");
+ }
+
+ TreeItem *ti=files->create_item(root);
+ ti->set_text(0,dep);
+ ti->set_icon(0,icon);
+
+ }
+
+ popup_centered_minsize(Size2(500,220));
+
+}
+
+void DependencyErrorDialog::ok_pressed() {
+
+ EditorNode::get_singleton()->load_scene(for_file,true);
+}
+
+void DependencyErrorDialog::custom_action(const String&) {
+
+ EditorNode::get_singleton()->fix_dependencies(for_file);
+}
+
+DependencyErrorDialog::DependencyErrorDialog() {
+
+ VBoxContainer *vb = memnew( VBoxContainer );
+ add_child(vb);
+ set_child_rect(vb);
+
+
+ files = memnew( Tree );
+ files->set_hide_root(true);
+ vb->add_margin_child("Scene failed to load due to missing dependencies:",files,true);
+ files->set_v_size_flags(SIZE_EXPAND_FILL);
+ get_ok()->set_text("Open Anyway");
+
+ text = memnew( Label );
+ vb->add_child(text);
+ text->set_text("Which action should be taken?");
+
+
+ fdep=add_button("Fix Dependencies",true,"fixdeps");
+
+ set_title("Errors loading!");
+
+}
diff --git a/tools/editor/dependency_editor.h b/tools/editor/dependency_editor.h
new file mode 100644
index 0000000000..1c328e7a93
--- /dev/null
+++ b/tools/editor/dependency_editor.h
@@ -0,0 +1,94 @@
+#ifndef DEPENDENCY_EDITOR_H
+#define DEPENDENCY_EDITOR_H
+
+#include "scene/gui/dialogs.h"
+#include "scene/gui/tree.h"
+#include "scene/gui/tab_container.h"
+#include "editor_file_dialog.h"
+
+class EditorFileSystemDirectory;
+
+class DependencyEditor : public AcceptDialog {
+ OBJ_TYPE(DependencyEditor,AcceptDialog);
+
+
+ Tree *tree;
+ Button *fixdeps;
+
+ EditorFileDialog *search;
+
+ String replacing;
+ String editing;
+ List<String> missing;
+
+
+ void _fix_and_find(EditorFileSystemDirectory *efsd, Map<String,Map<String,String> >& candidates);
+
+ void _searched(const String& p_path);
+ void _load_pressed(Object* p_item,int p_cell,int p_button);
+ void _fix_all();
+ void _update_list();
+
+ void _update_file();
+
+protected:
+
+ static void _bind_methods();
+ void _notification(int p_what);
+public:
+
+
+ void edit(const String& p_path);
+ DependencyEditor();
+};
+
+class DependencyEditorOwners : public AcceptDialog {
+ OBJ_TYPE(DependencyEditorOwners,AcceptDialog);
+
+ ItemList *owners;
+ String editing;
+ void _fill_owners(EditorFileSystemDirectory *efsd);
+
+public:
+
+ void show(const String& p_path);
+ DependencyEditorOwners();
+};
+
+class DependencyRemoveDialog : public ConfirmationDialog {
+ OBJ_TYPE(DependencyRemoveDialog,ConfirmationDialog);
+
+
+ Label *text;
+ Tree *owners;
+ bool exist;
+ Map<String,TreeItem*> files;
+ void _fill_owners(EditorFileSystemDirectory *efsd);
+
+ void ok_pressed();
+
+public:
+
+ void show(const Vector<String> &to_erase);
+ DependencyRemoveDialog();
+};
+
+
+class DependencyErrorDialog : public ConfirmationDialog {
+ OBJ_TYPE(DependencyErrorDialog,ConfirmationDialog);
+
+
+ String for_file;
+ Button *fdep;
+ Label *text;
+ Tree *files;
+ void ok_pressed();
+ void custom_action(const String&);
+
+public:
+
+ void show(const String& p_for,const Vector<String> &report);
+ DependencyErrorDialog();
+};
+
+#endif // DEPENDENCY_EDITOR_H
diff --git a/tools/editor/doc_code_font.h b/tools/editor/doc_code_font.h
index 470054ccd4..91f67c4a41 100644
--- a/tools/editor/doc_code_font.h
+++ b/tools/editor/doc_code_font.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -26,7 +26,7 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-static const int _bi_font_doc_code_font_height=19;
-static const int _bi_font_doc_code_font_ascent=15;
-static const int _bi_font_doc_code_font_charcount=193;
-static const int _bi_font_doc_code_font_characters[]={0, 253, 53, 2, 2, -1, 14, 0, 13, 253, 56, 2, 2, -1, 14, 0, 32, 253, 59, 2, 2, -1, 14, 4, 33, 221, 29, 3, 12, 1, 3, 5, 34, 144, 77, 6, 5, 0, 3, 7, 35, 180, 55, 9, 11, -1, 4, 8, 36, 215, 0, 8, 15, 0, 2, 8, 37, 28, 45, 12, 11, -1, 4, 12, 38, 199, 16, 11, 12, 0, 3, 11, 39, 157, 77, 3, 5, 0, 3, 4, 40, 36, 16, 4, 15, 0, 3, 5, 41, 31, 16, 4, 15, 0, 3, 5, 42, 93, 79, 9, 7, -1, 3, 8, 43, 110, 68, 9, 9, -1, 5, 8, 44, 121, 78, 4, 6, -1, 12, 4, 45, 204, 76, 5, 3, -1, 9, 5, 46, 216, 75, 4, 3, 0, 12, 4, 47, 233, 0, 8, 15, -1, 2, 7, 48, 246, 29, 9, 11, -1, 4, 8, 49, 208, 54, 8, 11, 0, 4, 8, 50, 219, 42, 9, 11, -1, 4, 8, 51, 229, 41, 9, 11, -1, 4, 8, 52, 239, 41, 9, 11, -1, 4, 8, 53, 0, 57, 9, 11, -1, 4, 8, 54, 160, 55, 9, 11, -1, 4, 8, 55, 10, 57, 9, 11, -1, 4, 8, 56, 20, 57, 9, 11, -1, 4, 8, 57, 40, 57, 9, 11, -1, 4, 8, 58, 78, 80, 3, 8, 0, 7, 4, 59, 72, 68, 4, 11, -1, 7, 4, 60, 18, 69, 8, 11, 0, 4, 8, 61, 112, 78, 8, 7, 0, 6, 8, 62, 0, 69, 8, 11, 0, 4, 8, 63, 184, 30, 7, 12, 0, 3, 7, 64, 148, 16, 13, 13, 0, 4, 14, 65, 117, 44, 11, 11, -1, 4, 10, 66, 50, 57, 9, 11, 0, 4, 9, 67, 197, 42, 10, 11, -1, 4, 8, 68, 153, 43, 10, 11, 0, 4, 10, 69, 244, 53, 8, 11, 0, 4, 8, 70, 43, 69, 7, 11, 0, 4, 7, 71, 93, 44, 11, 11, -1, 4, 10, 72, 60, 56, 9, 11, 0, 4, 10, 73, 77, 68, 4, 11, 0, 4, 4, 74, 249, 41, 6, 11, -1, 4, 5, 75, 70, 56, 9, 11, 0, 4, 9, 76, 27, 69, 7, 11, 0, 4, 7, 77, 14, 45, 13, 11, 0, 4, 14, 78, 208, 42, 10, 11, 0, 4, 11, 79, 41, 45, 12, 11, -1, 4, 11, 80, 199, 54, 8, 11, 0, 4, 9, 81, 162, 16, 13, 13, -1, 4, 11, 82, 80, 56, 9, 11, 0, 4, 9, 83, 90, 56, 9, 11, -1, 4, 8, 84, 100, 56, 9, 11, -1, 4, 8, 85, 164, 43, 10, 11, 0, 4, 10, 86, 129, 44, 11, 11, -1, 4, 9, 87, 229, 29, 16, 11, -1, 4, 15, 88, 175, 43, 10, 11, -1, 4, 9, 89, 186, 43, 10, 11, -1, 4, 8, 90, 110, 56, 9, 11, -1, 4, 8, 91, 26, 16, 4, 15, 0, 3, 5, 92, 123, 0, 10, 15, -2, 2, 7, 93, 251, 0, 4, 15, 0, 3, 5, 94, 82, 79, 10, 7, -2, 4, 8, 95, 180, 76, 9, 3, -1, 15, 8, 96, 161, 77, 6, 4, -1, 3, 5, 97, 27, 81, 8, 8, -1, 7, 8, 98, 68, 31, 8, 12, 0, 3, 9, 99, 18, 81, 8, 8, -1, 7, 7, 100, 211, 16, 9, 12, -1, 3, 9, 101, 228, 66, 9, 8, -1, 7, 8, 102, 176, 30, 7, 12, -1, 3, 5, 103, 120, 56, 9, 11, -1, 7, 8, 104, 59, 31, 8, 12, 0, 3, 9, 105, 66, 68, 5, 11, -1, 4, 4, 106, 141, 16, 6, 14, -2, 4, 4, 107, 104, 31, 8, 12, 0, 3, 8, 108, 251, 16, 3, 12, 0, 3, 4, 109, 144, 68, 13, 8, 0, 7, 13, 110, 9, 81, 8, 8, 0, 7, 9, 111, 208, 66, 9, 8, -1, 7, 9, 112, 217, 54, 8, 11, 0, 7, 9, 113, 140, 56, 9, 11, -1, 7, 9, 114, 53, 81, 6, 8, 0, 7, 6, 115, 45, 81, 7, 8, -1, 7, 6, 116, 102, 68, 7, 10, -1, 5, 6, 117, 36, 81, 8, 8, 0, 7, 9, 118, 238, 65, 9, 8, -1, 7, 8, 119, 158, 68, 13, 8, -1, 7, 12, 120, 218, 66, 9, 8, -1, 7, 7, 121, 150, 56, 9, 11, -1, 7, 8, 122, 248, 65, 7, 8, -1, 7, 6, 123, 14, 16, 5, 15, 0, 3, 6, 124, 41, 16, 3, 15, 2, 3, 8, 125, 20, 16, 5, 15, 0, 3, 6, 126, 135, 78, 8, 5, -1, 4, 8, 160, 253, 62, 2, 2, -1, 14, 4, 161, 225, 29, 3, 12, 0, 5, 5, 162, 35, 69, 7, 11, 0, 4, 8, 163, 190, 55, 8, 11, 0, 4, 8, 164, 82, 68, 9, 10, -1, 5, 8, 165, 105, 44, 11, 11, -2, 4, 8, 166, 45, 16, 3, 15, 2, 3, 8, 167, 125, 16, 8, 14, 0, 3, 8, 168, 197, 76, 6, 3, 0, 4, 7, 169, 186, 16, 12, 12, 0, 3, 13, 170, 137, 68, 6, 9, 0, 4, 7, 171, 197, 67, 10, 8, -1, 6, 9, 172, 126, 78, 8, 5, -1, 8, 8, 173, 210, 75, 5, 3, -1, 9, 5, 174, 103, 79, 8, 7, 0, 3, 8, 175, 190, 76, 6, 3, 0, 4, 6, 176, 151, 77, 5, 5, 0, 3, 5, 177, 221, 16, 9, 12, -1, 3, 8, 178, 60, 81, 6, 8, -1, 2, 5, 179, 67, 80, 5, 8, -1, 2, 5, 180, 168, 77, 6, 4, -1, 3, 5, 181, 170, 55, 9, 11, 0, 7, 9, 182, 176, 16, 9, 13, 0, 4, 10, 183, 221, 75, 3, 3, 0, 9, 4, 184, 175, 76, 4, 4, 0, 14, 5, 185, 73, 80, 4, 8, 0, 2, 4, 186, 129, 68, 7, 9, 0, 4, 7, 187, 185, 67, 11, 8, -1, 6, 9, 188, 54, 44, 12, 11, -2, 4, 11, 189, 67, 44, 12, 11, -2, 4, 11, 190, 80, 44, 12, 11, -1, 4, 11, 191, 192, 29, 7, 12, -1, 5, 7, 192, 88, 0, 11, 15, -1, 0, 10, 193, 64, 0, 11, 15, -1, 0, 10, 194, 76, 0, 11, 15, -1, 0, 10, 195, 100, 0, 11, 15, -1, 0, 10, 196, 74, 16, 11, 14, -1, 1, 10, 197, 62, 16, 11, 14, -1, 1, 10, 198, 0, 45, 13, 11, -1, 4, 12, 199, 107, 16, 8, 14, 0, 4, 8, 200, 188, 0, 8, 15, 0, 0, 8, 201, 206, 0, 8, 15, 0, 0, 8, 202, 224, 0, 8, 15, 0, 0, 8, 203, 116, 16, 8, 14, 0, 1, 8, 204, 0, 16, 6, 15, -2, 0, 4, 205, 7, 16, 6, 15, -1, 0, 4, 206, 242, 0, 8, 15, -2, 0, 4, 207, 134, 16, 6, 14, -1, 1, 4, 208, 141, 44, 11, 11, -1, 4, 10, 209, 145, 0, 10, 15, 0, 0, 11, 210, 0, 0, 12, 15, -1, 0, 11, 211, 39, 0, 12, 15, -1, 0, 11, 212, 26, 0, 12, 15, -1, 0, 11, 213, 13, 0, 12, 15, -1, 0, 11, 214, 49, 16, 12, 14, -1, 1, 11, 215, 0, 81, 8, 8, 0, 6, 8, 216, 52, 0, 11, 15, 0, 2, 11, 217, 167, 0, 10, 15, 0, 0, 10, 218, 134, 0, 10, 15, 0, 0, 10, 219, 156, 0, 10, 15, 0, 0, 10, 220, 86, 16, 10, 14, 0, 1, 10, 221, 112, 0, 10, 15, -1, 0, 8, 222, 226, 54, 8, 11, 0, 4, 9, 223, 77, 31, 8, 12, 0, 3, 9, 224, 113, 31, 8, 12, -1, 3, 8, 225, 122, 31, 8, 12, -1, 3, 8, 226, 131, 31, 8, 12, -1, 3, 8, 227, 167, 30, 8, 12, -1, 3, 8, 228, 235, 53, 8, 11, -1, 4, 8, 229, 50, 31, 8, 12, -1, 3, 8, 230, 172, 67, 12, 8, 0, 7, 12, 231, 51, 69, 7, 11, 0, 7, 7, 232, 40, 32, 9, 12, -1, 3, 8, 233, 231, 16, 9, 12, -1, 3, 8, 234, 241, 16, 9, 12, -1, 3, 8, 235, 30, 57, 9, 11, -1, 4, 8, 236, 200, 29, 6, 12, -2, 3, 4, 237, 214, 29, 6, 12, -1, 3, 4, 238, 207, 29, 6, 12, -1, 3, 4, 239, 59, 69, 6, 11, -1, 4, 4, 240, 86, 31, 8, 12, 0, 3, 9, 241, 95, 31, 8, 12, 0, 3, 9, 242, 0, 32, 9, 12, -1, 3, 9, 243, 10, 32, 9, 12, -1, 3, 9, 244, 20, 32, 9, 12, -1, 3, 9, 245, 30, 32, 9, 12, -1, 3, 9, 246, 130, 56, 9, 11, -1, 4, 9, 247, 120, 68, 8, 9, -1, 5, 8, 248, 92, 68, 9, 10, 0, 6, 9, 249, 140, 31, 8, 12, 0, 3, 9, 250, 149, 30, 8, 12, 0, 3, 9, 251, 158, 30, 8, 12, 0, 3, 9, 252, 9, 69, 8, 11, 0, 4, 9, 253, 178, 0, 9, 15, -1, 3, 8, 254, 197, 0, 8, 15, 0, 3, 9, 255, 97, 16, 9, 14, -1, 4, 8};
+static const int _bi_font_doc_code_font_height=19;
+static const int _bi_font_doc_code_font_ascent=15;
+static const int _bi_font_doc_code_font_charcount=193;
+static const int _bi_font_doc_code_font_characters[]={0, 253, 53, 2, 2, -1, 14, 0, 13, 253, 56, 2, 2, -1, 14, 0, 32, 253, 59, 2, 2, -1, 14, 4, 33, 221, 29, 3, 12, 1, 3, 5, 34, 144, 77, 6, 5, 0, 3, 7, 35, 180, 55, 9, 11, -1, 4, 8, 36, 215, 0, 8, 15, 0, 2, 8, 37, 28, 45, 12, 11, -1, 4, 12, 38, 199, 16, 11, 12, 0, 3, 11, 39, 157, 77, 3, 5, 0, 3, 4, 40, 36, 16, 4, 15, 0, 3, 5, 41, 31, 16, 4, 15, 0, 3, 5, 42, 93, 79, 9, 7, -1, 3, 8, 43, 110, 68, 9, 9, -1, 5, 8, 44, 121, 78, 4, 6, -1, 12, 4, 45, 204, 76, 5, 3, -1, 9, 5, 46, 216, 75, 4, 3, 0, 12, 4, 47, 233, 0, 8, 15, -1, 2, 7, 48, 246, 29, 9, 11, -1, 4, 8, 49, 208, 54, 8, 11, 0, 4, 8, 50, 219, 42, 9, 11, -1, 4, 8, 51, 229, 41, 9, 11, -1, 4, 8, 52, 239, 41, 9, 11, -1, 4, 8, 53, 0, 57, 9, 11, -1, 4, 8, 54, 160, 55, 9, 11, -1, 4, 8, 55, 10, 57, 9, 11, -1, 4, 8, 56, 20, 57, 9, 11, -1, 4, 8, 57, 40, 57, 9, 11, -1, 4, 8, 58, 78, 80, 3, 8, 0, 7, 4, 59, 72, 68, 4, 11, -1, 7, 4, 60, 18, 69, 8, 11, 0, 4, 8, 61, 112, 78, 8, 7, 0, 6, 8, 62, 0, 69, 8, 11, 0, 4, 8, 63, 184, 30, 7, 12, 0, 3, 7, 64, 148, 16, 13, 13, 0, 4, 14, 65, 117, 44, 11, 11, -1, 4, 10, 66, 50, 57, 9, 11, 0, 4, 9, 67, 197, 42, 10, 11, -1, 4, 8, 68, 153, 43, 10, 11, 0, 4, 10, 69, 244, 53, 8, 11, 0, 4, 8, 70, 43, 69, 7, 11, 0, 4, 7, 71, 93, 44, 11, 11, -1, 4, 10, 72, 60, 56, 9, 11, 0, 4, 10, 73, 77, 68, 4, 11, 0, 4, 4, 74, 249, 41, 6, 11, -1, 4, 5, 75, 70, 56, 9, 11, 0, 4, 9, 76, 27, 69, 7, 11, 0, 4, 7, 77, 14, 45, 13, 11, 0, 4, 14, 78, 208, 42, 10, 11, 0, 4, 11, 79, 41, 45, 12, 11, -1, 4, 11, 80, 199, 54, 8, 11, 0, 4, 9, 81, 162, 16, 13, 13, -1, 4, 11, 82, 80, 56, 9, 11, 0, 4, 9, 83, 90, 56, 9, 11, -1, 4, 8, 84, 100, 56, 9, 11, -1, 4, 8, 85, 164, 43, 10, 11, 0, 4, 10, 86, 129, 44, 11, 11, -1, 4, 9, 87, 229, 29, 16, 11, -1, 4, 15, 88, 175, 43, 10, 11, -1, 4, 9, 89, 186, 43, 10, 11, -1, 4, 8, 90, 110, 56, 9, 11, -1, 4, 8, 91, 26, 16, 4, 15, 0, 3, 5, 92, 123, 0, 10, 15, -2, 2, 7, 93, 251, 0, 4, 15, 0, 3, 5, 94, 82, 79, 10, 7, -2, 4, 8, 95, 180, 76, 9, 3, -1, 15, 8, 96, 161, 77, 6, 4, -1, 3, 5, 97, 27, 81, 8, 8, -1, 7, 8, 98, 68, 31, 8, 12, 0, 3, 9, 99, 18, 81, 8, 8, -1, 7, 7, 100, 211, 16, 9, 12, -1, 3, 9, 101, 228, 66, 9, 8, -1, 7, 8, 102, 176, 30, 7, 12, -1, 3, 5, 103, 120, 56, 9, 11, -1, 7, 8, 104, 59, 31, 8, 12, 0, 3, 9, 105, 66, 68, 5, 11, -1, 4, 4, 106, 141, 16, 6, 14, -2, 4, 4, 107, 104, 31, 8, 12, 0, 3, 8, 108, 251, 16, 3, 12, 0, 3, 4, 109, 144, 68, 13, 8, 0, 7, 13, 110, 9, 81, 8, 8, 0, 7, 9, 111, 208, 66, 9, 8, -1, 7, 9, 112, 217, 54, 8, 11, 0, 7, 9, 113, 140, 56, 9, 11, -1, 7, 9, 114, 53, 81, 6, 8, 0, 7, 6, 115, 45, 81, 7, 8, -1, 7, 6, 116, 102, 68, 7, 10, -1, 5, 6, 117, 36, 81, 8, 8, 0, 7, 9, 118, 238, 65, 9, 8, -1, 7, 8, 119, 158, 68, 13, 8, -1, 7, 12, 120, 218, 66, 9, 8, -1, 7, 7, 121, 150, 56, 9, 11, -1, 7, 8, 122, 248, 65, 7, 8, -1, 7, 6, 123, 14, 16, 5, 15, 0, 3, 6, 124, 41, 16, 3, 15, 2, 3, 8, 125, 20, 16, 5, 15, 0, 3, 6, 126, 135, 78, 8, 5, -1, 4, 8, 160, 253, 62, 2, 2, -1, 14, 4, 161, 225, 29, 3, 12, 0, 5, 5, 162, 35, 69, 7, 11, 0, 4, 8, 163, 190, 55, 8, 11, 0, 4, 8, 164, 82, 68, 9, 10, -1, 5, 8, 165, 105, 44, 11, 11, -2, 4, 8, 166, 45, 16, 3, 15, 2, 3, 8, 167, 125, 16, 8, 14, 0, 3, 8, 168, 197, 76, 6, 3, 0, 4, 7, 169, 186, 16, 12, 12, 0, 3, 13, 170, 137, 68, 6, 9, 0, 4, 7, 171, 197, 67, 10, 8, -1, 6, 9, 172, 126, 78, 8, 5, -1, 8, 8, 173, 210, 75, 5, 3, -1, 9, 5, 174, 103, 79, 8, 7, 0, 3, 8, 175, 190, 76, 6, 3, 0, 4, 6, 176, 151, 77, 5, 5, 0, 3, 5, 177, 221, 16, 9, 12, -1, 3, 8, 178, 60, 81, 6, 8, -1, 2, 5, 179, 67, 80, 5, 8, -1, 2, 5, 180, 168, 77, 6, 4, -1, 3, 5, 181, 170, 55, 9, 11, 0, 7, 9, 182, 176, 16, 9, 13, 0, 4, 10, 183, 221, 75, 3, 3, 0, 9, 4, 184, 175, 76, 4, 4, 0, 14, 5, 185, 73, 80, 4, 8, 0, 2, 4, 186, 129, 68, 7, 9, 0, 4, 7, 187, 185, 67, 11, 8, -1, 6, 9, 188, 54, 44, 12, 11, -2, 4, 11, 189, 67, 44, 12, 11, -2, 4, 11, 190, 80, 44, 12, 11, -1, 4, 11, 191, 192, 29, 7, 12, -1, 5, 7, 192, 88, 0, 11, 15, -1, 0, 10, 193, 64, 0, 11, 15, -1, 0, 10, 194, 76, 0, 11, 15, -1, 0, 10, 195, 100, 0, 11, 15, -1, 0, 10, 196, 74, 16, 11, 14, -1, 1, 10, 197, 62, 16, 11, 14, -1, 1, 10, 198, 0, 45, 13, 11, -1, 4, 12, 199, 107, 16, 8, 14, 0, 4, 8, 200, 188, 0, 8, 15, 0, 0, 8, 201, 206, 0, 8, 15, 0, 0, 8, 202, 224, 0, 8, 15, 0, 0, 8, 203, 116, 16, 8, 14, 0, 1, 8, 204, 0, 16, 6, 15, -2, 0, 4, 205, 7, 16, 6, 15, -1, 0, 4, 206, 242, 0, 8, 15, -2, 0, 4, 207, 134, 16, 6, 14, -1, 1, 4, 208, 141, 44, 11, 11, -1, 4, 10, 209, 145, 0, 10, 15, 0, 0, 11, 210, 0, 0, 12, 15, -1, 0, 11, 211, 39, 0, 12, 15, -1, 0, 11, 212, 26, 0, 12, 15, -1, 0, 11, 213, 13, 0, 12, 15, -1, 0, 11, 214, 49, 16, 12, 14, -1, 1, 11, 215, 0, 81, 8, 8, 0, 6, 8, 216, 52, 0, 11, 15, 0, 2, 11, 217, 167, 0, 10, 15, 0, 0, 10, 218, 134, 0, 10, 15, 0, 0, 10, 219, 156, 0, 10, 15, 0, 0, 10, 220, 86, 16, 10, 14, 0, 1, 10, 221, 112, 0, 10, 15, -1, 0, 8, 222, 226, 54, 8, 11, 0, 4, 9, 223, 77, 31, 8, 12, 0, 3, 9, 224, 113, 31, 8, 12, -1, 3, 8, 225, 122, 31, 8, 12, -1, 3, 8, 226, 131, 31, 8, 12, -1, 3, 8, 227, 167, 30, 8, 12, -1, 3, 8, 228, 235, 53, 8, 11, -1, 4, 8, 229, 50, 31, 8, 12, -1, 3, 8, 230, 172, 67, 12, 8, 0, 7, 12, 231, 51, 69, 7, 11, 0, 7, 7, 232, 40, 32, 9, 12, -1, 3, 8, 233, 231, 16, 9, 12, -1, 3, 8, 234, 241, 16, 9, 12, -1, 3, 8, 235, 30, 57, 9, 11, -1, 4, 8, 236, 200, 29, 6, 12, -2, 3, 4, 237, 214, 29, 6, 12, -1, 3, 4, 238, 207, 29, 6, 12, -1, 3, 4, 239, 59, 69, 6, 11, -1, 4, 4, 240, 86, 31, 8, 12, 0, 3, 9, 241, 95, 31, 8, 12, 0, 3, 9, 242, 0, 32, 9, 12, -1, 3, 9, 243, 10, 32, 9, 12, -1, 3, 9, 244, 20, 32, 9, 12, -1, 3, 9, 245, 30, 32, 9, 12, -1, 3, 9, 246, 130, 56, 9, 11, -1, 4, 9, 247, 120, 68, 8, 9, -1, 5, 8, 248, 92, 68, 9, 10, 0, 6, 9, 249, 140, 31, 8, 12, 0, 3, 9, 250, 149, 30, 8, 12, 0, 3, 9, 251, 158, 30, 8, 12, 0, 3, 9, 252, 9, 69, 8, 11, 0, 4, 9, 253, 178, 0, 9, 15, -1, 3, 8, 254, 197, 0, 8, 15, 0, 3, 9, 255, 97, 16, 9, 14, -1, 4, 8};
diff --git a/tools/editor/doc_font.h b/tools/editor/doc_font.h
index fe998cfb16..f2e5e7950b 100644
--- a/tools/editor/doc_font.h
+++ b/tools/editor/doc_font.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -26,7 +26,7 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-static const int _bi_font_doc_font_height=18;
-static const int _bi_font_doc_font_ascent=14;
-static const int _bi_font_doc_font_charcount=193;
-static const int _bi_font_doc_font_characters[]={0, 253, 3, 2, 2, -1, 13, 0, 13, 253, 9, 2, 2, -1, 13, 0, 32, 253, 0, 2, 2, -1, 13, 3, 33, 220, 26, 3, 11, 1, 3, 5, 34, 187, 59, 5, 5, 0, 3, 6, 35, 88, 40, 9, 10, -1, 4, 7, 36, 50, 0, 8, 14, -1, 2, 7, 37, 175, 14, 11, 11, -1, 3, 11, 38, 187, 14, 11, 11, -1, 3, 10, 39, 198, 58, 3, 5, 0, 3, 3, 40, 98, 0, 4, 14, 0, 3, 5, 41, 103, 0, 4, 14, 0, 3, 5, 42, 146, 61, 7, 6, 0, 3, 7, 43, 246, 48, 8, 8, -1, 5, 7, 44, 193, 58, 4, 5, -1, 11, 4, 45, 13, 73, 5, 2, 0, 9, 5, 46, 241, 57, 3, 3, 0, 11, 4, 47, 84, 0, 7, 14, -1, 2, 6, 48, 246, 37, 8, 10, -1, 4, 7, 49, 33, 52, 7, 10, 0, 4, 7, 50, 237, 37, 8, 10, -1, 4, 7, 51, 219, 38, 8, 10, -1, 4, 7, 52, 128, 39, 9, 10, -1, 4, 7, 53, 41, 52, 7, 10, -1, 4, 7, 54, 9, 53, 7, 10, 0, 4, 7, 55, 228, 38, 8, 10, -1, 4, 7, 56, 57, 52, 7, 10, 0, 4, 7, 57, 65, 51, 7, 10, 0, 4, 7, 58, 98, 62, 4, 8, 0, 6, 4, 59, 162, 50, 5, 10, -1, 6, 4, 60, 174, 49, 8, 9, -1, 4, 7, 61, 179, 59, 7, 5, 0, 7, 7, 62, 228, 49, 8, 8, -1, 5, 7, 63, 89, 28, 7, 11, 0, 3, 7, 64, 54, 15, 12, 12, 0, 4, 13, 65, 36, 41, 10, 10, -1, 4, 9, 66, 97, 51, 7, 10, 0, 4, 8, 67, 174, 38, 8, 10, 0, 4, 8, 68, 156, 39, 8, 10, 0, 4, 9, 69, 105, 51, 7, 10, 0, 4, 7, 70, 121, 51, 6, 10, 0, 4, 7, 71, 0, 53, 8, 10, 0, 4, 9, 72, 192, 38, 8, 10, 0, 4, 9, 73, 251, 26, 3, 10, 0, 4, 4, 74, 156, 50, 5, 10, -1, 4, 5, 75, 147, 39, 8, 10, 0, 4, 8, 76, 135, 50, 6, 10, 0, 4, 6, 77, 13, 41, 11, 10, 0, 4, 12, 78, 210, 38, 8, 10, 0, 4, 10, 79, 68, 40, 9, 10, 0, 4, 10, 80, 113, 51, 7, 10, 0, 4, 8, 81, 199, 14, 10, 11, 0, 4, 10, 82, 183, 38, 8, 10, 0, 4, 8, 83, 128, 50, 6, 10, 0, 4, 7, 84, 108, 40, 9, 10, -1, 4, 7, 85, 165, 39, 8, 10, 0, 4, 9, 86, 25, 41, 10, 10, -1, 4, 9, 87, 236, 26, 14, 10, -1, 4, 13, 88, 98, 40, 9, 10, -1, 4, 8, 89, 118, 40, 9, 10, -1, 4, 7, 90, 138, 39, 8, 10, -1, 4, 7, 91, 49, 15, 4, 13, 0, 3, 5, 92, 76, 0, 7, 14, -1, 2, 6, 93, 43, 15, 5, 13, -1, 3, 5, 94, 137, 61, 8, 6, -1, 4, 7, 95, 245, 57, 9, 2, -1, 15, 7, 96, 230, 58, 5, 3, -1, 3, 4, 97, 57, 63, 7, 8, 0, 6, 7, 98, 81, 28, 7, 11, 0, 3, 8, 99, 79, 62, 6, 8, 0, 6, 6, 100, 33, 29, 7, 11, 0, 3, 8, 101, 9, 64, 7, 8, 0, 6, 8, 102, 200, 26, 6, 11, -1, 3, 5, 103, 153, 27, 7, 11, 0, 6, 7, 104, 121, 27, 7, 11, 0, 3, 8, 105, 224, 26, 3, 11, 0, 3, 4, 106, 92, 0, 5, 14, -2, 3, 4, 107, 25, 29, 7, 11, 0, 3, 7, 108, 228, 26, 3, 11, 0, 3, 4, 109, 204, 49, 11, 8, 0, 6, 12, 110, 17, 63, 7, 8, 0, 6, 8, 111, 33, 63, 7, 8, 0, 6, 8, 112, 17, 29, 7, 11, 0, 6, 8, 113, 9, 29, 7, 11, 0, 6, 8, 114, 86, 62, 5, 8, 0, 6, 5, 115, 92, 62, 5, 8, 0, 6, 6, 116, 168, 50, 5, 10, -1, 4, 5, 117, 49, 63, 7, 8, 0, 6, 8, 118, 0, 64, 8, 8, -1, 6, 7, 119, 216, 49, 11, 8, -1, 6, 10, 120, 41, 63, 7, 8, -1, 6, 7, 121, 0, 30, 8, 11, -1, 6, 7, 122, 72, 62, 6, 8, 0, 6, 6, 123, 8, 15, 6, 13, -1, 3, 5, 124, 0, 0, 3, 15, 1, 2, 7, 125, 15, 15, 6, 13, -1, 3, 5, 126, 202, 58, 8, 4, -1, 4, 7, 160, 253, 6, 2, 2, -1, 13, 3, 161, 232, 26, 3, 11, 0, 4, 5, 162, 149, 50, 6, 10, 0, 4, 7, 163, 17, 52, 7, 10, 0, 4, 7, 164, 183, 49, 8, 9, -1, 5, 7, 165, 47, 41, 10, 10, -2, 4, 7, 166, 4, 0, 3, 15, 2, 2, 7, 167, 122, 14, 7, 12, 0, 3, 7, 168, 0, 73, 6, 2, -1, 4, 6, 169, 162, 14, 12, 11, 0, 3, 13, 170, 65, 62, 6, 8, -1, 4, 6, 171, 112, 62, 8, 7, -1, 6, 8, 172, 211, 58, 7, 4, -1, 8, 7, 173, 19, 72, 5, 2, 0, 9, 5, 174, 103, 62, 8, 7, -1, 3, 8, 175, 7, 73, 5, 2, 0, 3, 6, 176, 171, 61, 7, 5, -1, 3, 5, 177, 58, 40, 9, 10, -1, 4, 7, 178, 154, 61, 5, 6, 0, 2, 5, 179, 160, 61, 5, 6, -1, 2, 5, 180, 224, 58, 5, 3, -1, 3, 4, 181, 241, 14, 8, 11, 0, 6, 8, 182, 97, 15, 8, 12, 0, 4, 9, 183, 236, 58, 4, 3, 0, 8, 4, 184, 219, 58, 4, 4, 0, 13, 5, 185, 166, 61, 4, 6, 0, 2, 4, 186, 25, 63, 7, 8, -1, 4, 6, 187, 129, 61, 7, 7, 0, 6, 8, 188, 221, 14, 9, 11, 0, 3, 10, 189, 231, 14, 9, 11, 0, 3, 10, 190, 210, 14, 10, 11, -1, 3, 10, 191, 41, 29, 7, 11, -1, 4, 7, 192, 141, 0, 10, 13, -1, 1, 9, 193, 130, 0, 10, 13, -1, 1, 9, 194, 119, 0, 10, 13, -1, 1, 9, 195, 20, 0, 10, 14, -1, 0, 9, 196, 67, 15, 10, 12, -1, 2, 9, 197, 108, 0, 10, 13, -1, 1, 9, 198, 0, 42, 12, 10, -1, 4, 11, 199, 228, 0, 8, 13, 0, 4, 8, 200, 245, 0, 7, 13, 0, 1, 7, 201, 237, 0, 7, 13, 0, 1, 7, 202, 0, 16, 7, 13, 0, 1, 7, 203, 130, 14, 7, 12, 0, 2, 7, 204, 36, 15, 6, 13, -2, 1, 4, 205, 22, 15, 6, 13, -1, 1, 4, 206, 29, 15, 6, 13, -1, 1, 4, 207, 154, 14, 7, 12, -2, 2, 4, 208, 78, 40, 9, 10, -1, 4, 9, 209, 59, 0, 8, 14, 0, 0, 10, 210, 172, 0, 9, 13, 0, 1, 10, 211, 152, 0, 9, 13, 0, 1, 10, 212, 182, 0, 9, 13, 0, 1, 10, 213, 31, 0, 9, 14, 0, 0, 10, 214, 78, 15, 9, 12, 0, 2, 10, 215, 121, 62, 7, 7, 0, 6, 7, 216, 8, 0, 11, 14, -1, 2, 10, 217, 192, 0, 8, 13, 0, 1, 9, 218, 210, 0, 8, 13, 0, 1, 9, 219, 201, 0, 8, 13, 0, 1, 9, 220, 88, 15, 8, 12, 0, 2, 9, 221, 162, 0, 9, 13, -1, 1, 7, 222, 81, 51, 7, 10, 0, 4, 8, 223, 49, 29, 7, 11, 0, 3, 8, 224, 57, 28, 7, 11, 0, 3, 7, 225, 65, 28, 7, 11, 0, 3, 7, 226, 73, 28, 7, 11, 0, 3, 7, 227, 138, 14, 7, 12, 0, 2, 7, 228, 25, 52, 7, 10, 0, 4, 7, 229, 146, 14, 7, 12, 0, 2, 7, 230, 192, 49, 11, 8, 0, 6, 12, 231, 207, 26, 6, 11, 0, 6, 6, 232, 97, 28, 7, 11, 0, 3, 8, 233, 105, 28, 7, 11, 0, 3, 8, 234, 113, 28, 7, 11, 0, 3, 8, 235, 73, 51, 7, 10, 0, 4, 8, 236, 214, 26, 5, 11, -2, 3, 3, 237, 250, 14, 5, 11, -1, 3, 3, 238, 193, 26, 6, 11, -2, 3, 3, 239, 142, 50, 6, 10, -2, 4, 3, 240, 185, 26, 7, 11, 0, 3, 8, 241, 106, 15, 7, 12, 0, 2, 8, 242, 129, 27, 7, 11, 0, 3, 8, 243, 137, 27, 7, 11, 0, 3, 8, 244, 145, 27, 7, 11, 0, 3, 8, 245, 114, 14, 7, 12, 0, 2, 8, 246, 49, 52, 7, 10, 0, 4, 8, 247, 237, 48, 8, 8, -1, 5, 7, 248, 201, 38, 8, 10, 0, 5, 8, 249, 161, 27, 7, 11, 0, 3, 8, 250, 169, 26, 7, 11, 0, 3, 8, 251, 177, 26, 7, 11, 0, 3, 8, 252, 89, 51, 7, 10, 0, 4, 8, 253, 41, 0, 8, 14, -1, 3, 7, 254, 68, 0, 7, 14, 0, 3, 8, 255, 219, 0, 8, 13, -1, 4, 7};
+static const int _bi_font_doc_font_height=18;
+static const int _bi_font_doc_font_ascent=14;
+static const int _bi_font_doc_font_charcount=193;
+static const int _bi_font_doc_font_characters[]={0, 253, 3, 2, 2, -1, 13, 0, 13, 253, 9, 2, 2, -1, 13, 0, 32, 253, 0, 2, 2, -1, 13, 3, 33, 220, 26, 3, 11, 1, 3, 5, 34, 187, 59, 5, 5, 0, 3, 6, 35, 88, 40, 9, 10, -1, 4, 7, 36, 50, 0, 8, 14, -1, 2, 7, 37, 175, 14, 11, 11, -1, 3, 11, 38, 187, 14, 11, 11, -1, 3, 10, 39, 198, 58, 3, 5, 0, 3, 3, 40, 98, 0, 4, 14, 0, 3, 5, 41, 103, 0, 4, 14, 0, 3, 5, 42, 146, 61, 7, 6, 0, 3, 7, 43, 246, 48, 8, 8, -1, 5, 7, 44, 193, 58, 4, 5, -1, 11, 4, 45, 13, 73, 5, 2, 0, 9, 5, 46, 241, 57, 3, 3, 0, 11, 4, 47, 84, 0, 7, 14, -1, 2, 6, 48, 246, 37, 8, 10, -1, 4, 7, 49, 33, 52, 7, 10, 0, 4, 7, 50, 237, 37, 8, 10, -1, 4, 7, 51, 219, 38, 8, 10, -1, 4, 7, 52, 128, 39, 9, 10, -1, 4, 7, 53, 41, 52, 7, 10, -1, 4, 7, 54, 9, 53, 7, 10, 0, 4, 7, 55, 228, 38, 8, 10, -1, 4, 7, 56, 57, 52, 7, 10, 0, 4, 7, 57, 65, 51, 7, 10, 0, 4, 7, 58, 98, 62, 4, 8, 0, 6, 4, 59, 162, 50, 5, 10, -1, 6, 4, 60, 174, 49, 8, 9, -1, 4, 7, 61, 179, 59, 7, 5, 0, 7, 7, 62, 228, 49, 8, 8, -1, 5, 7, 63, 89, 28, 7, 11, 0, 3, 7, 64, 54, 15, 12, 12, 0, 4, 13, 65, 36, 41, 10, 10, -1, 4, 9, 66, 97, 51, 7, 10, 0, 4, 8, 67, 174, 38, 8, 10, 0, 4, 8, 68, 156, 39, 8, 10, 0, 4, 9, 69, 105, 51, 7, 10, 0, 4, 7, 70, 121, 51, 6, 10, 0, 4, 7, 71, 0, 53, 8, 10, 0, 4, 9, 72, 192, 38, 8, 10, 0, 4, 9, 73, 251, 26, 3, 10, 0, 4, 4, 74, 156, 50, 5, 10, -1, 4, 5, 75, 147, 39, 8, 10, 0, 4, 8, 76, 135, 50, 6, 10, 0, 4, 6, 77, 13, 41, 11, 10, 0, 4, 12, 78, 210, 38, 8, 10, 0, 4, 10, 79, 68, 40, 9, 10, 0, 4, 10, 80, 113, 51, 7, 10, 0, 4, 8, 81, 199, 14, 10, 11, 0, 4, 10, 82, 183, 38, 8, 10, 0, 4, 8, 83, 128, 50, 6, 10, 0, 4, 7, 84, 108, 40, 9, 10, -1, 4, 7, 85, 165, 39, 8, 10, 0, 4, 9, 86, 25, 41, 10, 10, -1, 4, 9, 87, 236, 26, 14, 10, -1, 4, 13, 88, 98, 40, 9, 10, -1, 4, 8, 89, 118, 40, 9, 10, -1, 4, 7, 90, 138, 39, 8, 10, -1, 4, 7, 91, 49, 15, 4, 13, 0, 3, 5, 92, 76, 0, 7, 14, -1, 2, 6, 93, 43, 15, 5, 13, -1, 3, 5, 94, 137, 61, 8, 6, -1, 4, 7, 95, 245, 57, 9, 2, -1, 15, 7, 96, 230, 58, 5, 3, -1, 3, 4, 97, 57, 63, 7, 8, 0, 6, 7, 98, 81, 28, 7, 11, 0, 3, 8, 99, 79, 62, 6, 8, 0, 6, 6, 100, 33, 29, 7, 11, 0, 3, 8, 101, 9, 64, 7, 8, 0, 6, 8, 102, 200, 26, 6, 11, -1, 3, 5, 103, 153, 27, 7, 11, 0, 6, 7, 104, 121, 27, 7, 11, 0, 3, 8, 105, 224, 26, 3, 11, 0, 3, 4, 106, 92, 0, 5, 14, -2, 3, 4, 107, 25, 29, 7, 11, 0, 3, 7, 108, 228, 26, 3, 11, 0, 3, 4, 109, 204, 49, 11, 8, 0, 6, 12, 110, 17, 63, 7, 8, 0, 6, 8, 111, 33, 63, 7, 8, 0, 6, 8, 112, 17, 29, 7, 11, 0, 6, 8, 113, 9, 29, 7, 11, 0, 6, 8, 114, 86, 62, 5, 8, 0, 6, 5, 115, 92, 62, 5, 8, 0, 6, 6, 116, 168, 50, 5, 10, -1, 4, 5, 117, 49, 63, 7, 8, 0, 6, 8, 118, 0, 64, 8, 8, -1, 6, 7, 119, 216, 49, 11, 8, -1, 6, 10, 120, 41, 63, 7, 8, -1, 6, 7, 121, 0, 30, 8, 11, -1, 6, 7, 122, 72, 62, 6, 8, 0, 6, 6, 123, 8, 15, 6, 13, -1, 3, 5, 124, 0, 0, 3, 15, 1, 2, 7, 125, 15, 15, 6, 13, -1, 3, 5, 126, 202, 58, 8, 4, -1, 4, 7, 160, 253, 6, 2, 2, -1, 13, 3, 161, 232, 26, 3, 11, 0, 4, 5, 162, 149, 50, 6, 10, 0, 4, 7, 163, 17, 52, 7, 10, 0, 4, 7, 164, 183, 49, 8, 9, -1, 5, 7, 165, 47, 41, 10, 10, -2, 4, 7, 166, 4, 0, 3, 15, 2, 2, 7, 167, 122, 14, 7, 12, 0, 3, 7, 168, 0, 73, 6, 2, -1, 4, 6, 169, 162, 14, 12, 11, 0, 3, 13, 170, 65, 62, 6, 8, -1, 4, 6, 171, 112, 62, 8, 7, -1, 6, 8, 172, 211, 58, 7, 4, -1, 8, 7, 173, 19, 72, 5, 2, 0, 9, 5, 174, 103, 62, 8, 7, -1, 3, 8, 175, 7, 73, 5, 2, 0, 3, 6, 176, 171, 61, 7, 5, -1, 3, 5, 177, 58, 40, 9, 10, -1, 4, 7, 178, 154, 61, 5, 6, 0, 2, 5, 179, 160, 61, 5, 6, -1, 2, 5, 180, 224, 58, 5, 3, -1, 3, 4, 181, 241, 14, 8, 11, 0, 6, 8, 182, 97, 15, 8, 12, 0, 4, 9, 183, 236, 58, 4, 3, 0, 8, 4, 184, 219, 58, 4, 4, 0, 13, 5, 185, 166, 61, 4, 6, 0, 2, 4, 186, 25, 63, 7, 8, -1, 4, 6, 187, 129, 61, 7, 7, 0, 6, 8, 188, 221, 14, 9, 11, 0, 3, 10, 189, 231, 14, 9, 11, 0, 3, 10, 190, 210, 14, 10, 11, -1, 3, 10, 191, 41, 29, 7, 11, -1, 4, 7, 192, 141, 0, 10, 13, -1, 1, 9, 193, 130, 0, 10, 13, -1, 1, 9, 194, 119, 0, 10, 13, -1, 1, 9, 195, 20, 0, 10, 14, -1, 0, 9, 196, 67, 15, 10, 12, -1, 2, 9, 197, 108, 0, 10, 13, -1, 1, 9, 198, 0, 42, 12, 10, -1, 4, 11, 199, 228, 0, 8, 13, 0, 4, 8, 200, 245, 0, 7, 13, 0, 1, 7, 201, 237, 0, 7, 13, 0, 1, 7, 202, 0, 16, 7, 13, 0, 1, 7, 203, 130, 14, 7, 12, 0, 2, 7, 204, 36, 15, 6, 13, -2, 1, 4, 205, 22, 15, 6, 13, -1, 1, 4, 206, 29, 15, 6, 13, -1, 1, 4, 207, 154, 14, 7, 12, -2, 2, 4, 208, 78, 40, 9, 10, -1, 4, 9, 209, 59, 0, 8, 14, 0, 0, 10, 210, 172, 0, 9, 13, 0, 1, 10, 211, 152, 0, 9, 13, 0, 1, 10, 212, 182, 0, 9, 13, 0, 1, 10, 213, 31, 0, 9, 14, 0, 0, 10, 214, 78, 15, 9, 12, 0, 2, 10, 215, 121, 62, 7, 7, 0, 6, 7, 216, 8, 0, 11, 14, -1, 2, 10, 217, 192, 0, 8, 13, 0, 1, 9, 218, 210, 0, 8, 13, 0, 1, 9, 219, 201, 0, 8, 13, 0, 1, 9, 220, 88, 15, 8, 12, 0, 2, 9, 221, 162, 0, 9, 13, -1, 1, 7, 222, 81, 51, 7, 10, 0, 4, 8, 223, 49, 29, 7, 11, 0, 3, 8, 224, 57, 28, 7, 11, 0, 3, 7, 225, 65, 28, 7, 11, 0, 3, 7, 226, 73, 28, 7, 11, 0, 3, 7, 227, 138, 14, 7, 12, 0, 2, 7, 228, 25, 52, 7, 10, 0, 4, 7, 229, 146, 14, 7, 12, 0, 2, 7, 230, 192, 49, 11, 8, 0, 6, 12, 231, 207, 26, 6, 11, 0, 6, 6, 232, 97, 28, 7, 11, 0, 3, 8, 233, 105, 28, 7, 11, 0, 3, 8, 234, 113, 28, 7, 11, 0, 3, 8, 235, 73, 51, 7, 10, 0, 4, 8, 236, 214, 26, 5, 11, -2, 3, 3, 237, 250, 14, 5, 11, -1, 3, 3, 238, 193, 26, 6, 11, -2, 3, 3, 239, 142, 50, 6, 10, -2, 4, 3, 240, 185, 26, 7, 11, 0, 3, 8, 241, 106, 15, 7, 12, 0, 2, 8, 242, 129, 27, 7, 11, 0, 3, 8, 243, 137, 27, 7, 11, 0, 3, 8, 244, 145, 27, 7, 11, 0, 3, 8, 245, 114, 14, 7, 12, 0, 2, 8, 246, 49, 52, 7, 10, 0, 4, 8, 247, 237, 48, 8, 8, -1, 5, 7, 248, 201, 38, 8, 10, 0, 5, 8, 249, 161, 27, 7, 11, 0, 3, 8, 250, 169, 26, 7, 11, 0, 3, 8, 251, 177, 26, 7, 11, 0, 3, 8, 252, 89, 51, 7, 10, 0, 4, 8, 253, 41, 0, 8, 14, -1, 3, 7, 254, 68, 0, 7, 14, 0, 3, 8, 255, 219, 0, 8, 13, -1, 4, 7};
diff --git a/tools/editor/doc_title_font.h b/tools/editor/doc_title_font.h
index 22bad995f8..fb6b4eaf5b 100644
--- a/tools/editor/doc_title_font.h
+++ b/tools/editor/doc_title_font.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -26,7 +26,7 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-static const int _bi_font_doc_title_font_height=22;
-static const int _bi_font_doc_title_font_ascent=17;
-static const int _bi_font_doc_title_font_charcount=193;
-static const int _bi_font_doc_title_font_characters[]={0, 108, 97, 2, 2, -1, 16, 0, 13, 111, 97, 2, 2, -1, 16, 0, 32, 252, 55, 2, 2, -1, 16, 4, 33, 84, 48, 3, 13, 1, 4, 6, 34, 9, 99, 6, 6, 0, 4, 8, 35, 52, 76, 9, 12, -1, 5, 9, 36, 31, 18, 9, 16, 0, 3, 9, 37, 28, 35, 13, 13, -1, 4, 13, 38, 56, 35, 12, 13, 0, 4, 13, 39, 16, 99, 3, 6, 0, 4, 4, 40, 48, 0, 5, 17, 0, 3, 6, 41, 54, 0, 5, 17, 0, 3, 6, 42, 207, 84, 9, 8, 0, 3, 9, 43, 121, 86, 9, 9, 0, 7, 9, 44, 251, 71, 4, 6, -1, 13, 5, 45, 102, 97, 5, 3, 0, 10, 6, 46, 66, 99, 4, 4, 0, 13, 5, 47, 24, 0, 9, 17, -2, 3, 8, 48, 11, 76, 10, 12, -1, 5, 9, 49, 102, 74, 9, 12, 0, 5, 9, 50, 48, 63, 10, 12, -1, 5, 9, 51, 59, 63, 10, 12, -1, 5, 9, 52, 70, 63, 10, 12, -1, 5, 9, 53, 81, 62, 10, 12, -1, 5, 9, 54, 235, 59, 10, 12, -1, 5, 9, 55, 103, 61, 10, 12, -1, 5, 9, 56, 114, 60, 10, 12, -1, 5, 9, 57, 125, 60, 10, 12, -1, 5, 9, 58, 252, 45, 3, 9, 0, 8, 5, 59, 246, 71, 4, 11, -1, 8, 5, 60, 148, 73, 8, 12, 0, 5, 9, 61, 0, 100, 8, 7, 0, 8, 9, 62, 139, 73, 8, 12, 0, 5, 9, 63, 40, 49, 8, 13, 0, 4, 8, 64, 184, 17, 15, 14, 0, 5, 16, 65, 178, 47, 12, 12, -1, 5, 11, 66, 136, 60, 10, 12, 0, 5, 10, 67, 240, 45, 11, 12, -1, 5, 10, 68, 0, 63, 11, 12, 0, 5, 11, 69, 130, 73, 8, 12, 0, 5, 9, 70, 112, 74, 8, 12, 0, 5, 8, 71, 191, 46, 12, 12, -1, 5, 11, 72, 24, 63, 11, 12, 0, 5, 11, 73, 193, 72, 4, 12, 0, 5, 5, 74, 173, 73, 6, 12, -1, 5, 6, 75, 147, 60, 10, 12, 0, 5, 10, 76, 121, 73, 8, 12, 0, 5, 8, 77, 122, 47, 15, 12, 0, 5, 16, 78, 12, 63, 11, 12, 0, 5, 12, 79, 138, 47, 13, 12, -1, 5, 12, 80, 62, 76, 9, 12, 0, 5, 10, 81, 200, 17, 14, 14, -1, 5, 12, 82, 158, 60, 10, 12, 0, 5, 10, 83, 32, 76, 9, 12, -1, 5, 9, 84, 169, 60, 10, 12, -1, 5, 9, 85, 204, 46, 11, 12, 0, 5, 12, 86, 165, 47, 12, 12, -1, 5, 11, 87, 88, 48, 17, 12, -1, 5, 16, 88, 216, 46, 11, 12, -1, 5, 10, 89, 228, 46, 11, 12, -1, 5, 9, 90, 180, 60, 10, 12, -1, 5, 9, 91, 249, 0, 5, 16, 0, 4, 6, 92, 13, 0, 10, 17, -2, 3, 8, 93, 85, 17, 5, 16, 0, 4, 6, 94, 196, 85, 10, 8, -1, 5, 9, 95, 71, 99, 10, 3, -1, 17, 9, 96, 46, 99, 6, 4, -1, 4, 5, 97, 111, 87, 9, 9, -1, 8, 9, 98, 207, 32, 9, 13, 0, 4, 10, 99, 161, 86, 8, 9, -1, 8, 8, 100, 69, 34, 10, 13, -1, 4, 10, 101, 89, 88, 10, 9, -1, 8, 9, 102, 58, 49, 7, 13, -1, 4, 6, 103, 191, 59, 10, 12, -1, 8, 9, 104, 177, 33, 9, 13, 0, 4, 10, 105, 187, 73, 5, 12, -1, 5, 4, 106, 177, 17, 6, 15, -2, 5, 5, 107, 217, 32, 9, 13, 0, 4, 9, 108, 250, 17, 4, 13, 0, 4, 4, 109, 24, 89, 14, 9, 0, 8, 15, 110, 151, 86, 9, 9, 0, 8, 10, 111, 100, 87, 10, 9, -1, 8, 10, 112, 42, 76, 9, 12, 0, 8, 10, 113, 213, 59, 10, 12, -1, 8, 10, 114, 188, 86, 7, 9, 0, 8, 6, 115, 170, 86, 8, 9, -1, 8, 7, 116, 230, 72, 7, 11, -1, 6, 6, 117, 141, 86, 9, 9, 0, 8, 10, 118, 78, 89, 10, 9, -1, 8, 9, 119, 8, 89, 15, 9, -1, 8, 13, 120, 131, 86, 9, 9, -1, 8, 8, 121, 224, 59, 10, 12, -1, 8, 9, 122, 179, 86, 8, 9, -1, 8, 7, 123, 34, 0, 6, 17, -1, 3, 6, 124, 64, 0, 3, 17, 2, 3, 9, 125, 41, 0, 6, 17, 0, 3, 6, 126, 30, 99, 9, 5, 0, 5, 9, 160, 114, 97, 2, 2, -1, 16, 4, 161, 80, 48, 3, 13, 1, 6, 6, 162, 165, 73, 7, 12, 0, 5, 9, 163, 92, 74, 9, 12, 0, 5, 9, 164, 209, 72, 10, 11, -1, 6, 9, 165, 36, 63, 11, 12, -2, 5, 9, 166, 60, 0, 3, 17, 2, 3, 9, 167, 151, 17, 8, 15, 0, 4, 9, 168, 82, 99, 6, 3, 0, 5, 7, 169, 236, 17, 13, 13, 0, 4, 15, 170, 238, 72, 7, 11, 0, 5, 7, 171, 54, 89, 11, 9, -1, 7, 10, 172, 20, 99, 9, 5, -1, 10, 9, 173, 96, 98, 5, 3, 0, 10, 6, 174, 226, 84, 8, 8, 0, 4, 9, 175, 89, 98, 6, 3, 0, 4, 7, 176, 40, 99, 5, 5, 0, 4, 6, 177, 22, 76, 9, 12, 0, 5, 9, 178, 235, 84, 6, 8, 0, 3, 6, 179, 242, 84, 6, 8, 0, 3, 6, 180, 53, 99, 6, 4, -1, 4, 5, 181, 0, 76, 10, 12, 0, 8, 10, 182, 215, 17, 10, 14, 0, 5, 11, 183, 251, 78, 3, 4, 0, 9, 5, 184, 60, 99, 5, 4, 0, 16, 5, 185, 249, 83, 5, 8, -1, 3, 5, 186, 0, 89, 7, 10, 0, 5, 8, 187, 66, 89, 11, 9, -1, 7, 10, 188, 42, 35, 13, 13, -1, 4, 12, 189, 0, 35, 13, 13, -1, 4, 12, 190, 14, 35, 13, 13, -1, 4, 13, 191, 49, 49, 8, 13, -1, 6, 8, 192, 150, 0, 12, 16, -1, 1, 11, 193, 176, 0, 12, 16, -1, 1, 11, 194, 163, 0, 12, 16, -1, 1, 11, 195, 124, 0, 12, 16, -1, 1, 11, 196, 105, 17, 12, 15, -1, 2, 11, 197, 0, 0, 12, 17, -1, 0, 11, 198, 106, 47, 15, 12, -1, 5, 14, 199, 141, 17, 9, 15, 0, 5, 10, 200, 60, 18, 8, 16, 0, 1, 9, 201, 41, 18, 9, 16, 0, 1, 9, 202, 21, 18, 9, 16, 0, 1, 9, 203, 160, 17, 8, 15, 0, 2, 9, 204, 69, 17, 7, 16, -2, 1, 5, 205, 77, 17, 7, 16, -1, 1, 5, 206, 51, 18, 8, 16, -2, 1, 5, 207, 169, 17, 7, 15, -2, 2, 5, 208, 152, 47, 12, 12, -1, 5, 12, 209, 237, 0, 11, 16, 0, 1, 12, 210, 82, 0, 13, 16, -1, 1, 12, 211, 68, 0, 13, 16, -1, 1, 12, 212, 110, 0, 13, 16, -1, 1, 12, 213, 96, 0, 13, 16, -1, 1, 12, 214, 91, 17, 13, 15, -1, 2, 12, 215, 217, 84, 8, 8, 0, 8, 9, 216, 137, 0, 12, 16, 0, 3, 12, 217, 225, 0, 11, 16, 0, 1, 12, 218, 189, 0, 11, 16, 0, 1, 12, 219, 213, 0, 11, 16, 0, 1, 12, 220, 118, 17, 11, 15, 0, 2, 12, 221, 201, 0, 11, 16, -1, 1, 9, 222, 72, 76, 9, 12, 0, 5, 10, 223, 167, 33, 9, 13, 0, 4, 10, 224, 157, 33, 9, 13, -1, 4, 9, 225, 30, 49, 9, 13, -1, 4, 9, 226, 227, 32, 9, 13, -1, 4, 9, 227, 237, 31, 9, 13, -1, 4, 9, 228, 82, 75, 9, 12, -1, 5, 9, 229, 226, 17, 9, 14, -1, 3, 9, 230, 39, 89, 14, 9, 0, 8, 14, 231, 157, 73, 7, 12, 0, 8, 8, 232, 146, 33, 10, 13, -1, 4, 9, 233, 80, 34, 10, 13, -1, 4, 9, 234, 91, 33, 10, 13, -1, 4, 9, 235, 92, 61, 10, 12, -1, 5, 9, 236, 66, 49, 6, 13, -2, 4, 4, 237, 73, 48, 6, 13, -1, 4, 4, 238, 247, 31, 8, 13, -2, 4, 4, 239, 180, 73, 6, 12, -1, 5, 4, 240, 187, 32, 9, 13, 0, 4, 10, 241, 197, 32, 9, 13, 0, 4, 10, 242, 102, 33, 10, 13, -1, 4, 10, 243, 113, 33, 10, 13, -1, 4, 10, 244, 124, 33, 10, 13, -1, 4, 10, 245, 135, 33, 10, 13, -1, 4, 10, 246, 202, 59, 10, 12, -1, 5, 10, 247, 220, 72, 9, 11, -1, 6, 9, 248, 198, 72, 10, 11, 0, 7, 10, 249, 0, 49, 9, 13, 0, 4, 10, 250, 10, 49, 9, 13, 0, 4, 10, 251, 20, 49, 9, 13, 0, 4, 10, 252, 246, 58, 9, 12, 0, 5, 10, 253, 0, 18, 10, 16, -1, 4, 9, 254, 11, 18, 9, 16, 0, 4, 10, 255, 130, 17, 10, 15, -1, 5, 9};
+static const int _bi_font_doc_title_font_height=22;
+static const int _bi_font_doc_title_font_ascent=17;
+static const int _bi_font_doc_title_font_charcount=193;
+static const int _bi_font_doc_title_font_characters[]={0, 108, 97, 2, 2, -1, 16, 0, 13, 111, 97, 2, 2, -1, 16, 0, 32, 252, 55, 2, 2, -1, 16, 4, 33, 84, 48, 3, 13, 1, 4, 6, 34, 9, 99, 6, 6, 0, 4, 8, 35, 52, 76, 9, 12, -1, 5, 9, 36, 31, 18, 9, 16, 0, 3, 9, 37, 28, 35, 13, 13, -1, 4, 13, 38, 56, 35, 12, 13, 0, 4, 13, 39, 16, 99, 3, 6, 0, 4, 4, 40, 48, 0, 5, 17, 0, 3, 6, 41, 54, 0, 5, 17, 0, 3, 6, 42, 207, 84, 9, 8, 0, 3, 9, 43, 121, 86, 9, 9, 0, 7, 9, 44, 251, 71, 4, 6, -1, 13, 5, 45, 102, 97, 5, 3, 0, 10, 6, 46, 66, 99, 4, 4, 0, 13, 5, 47, 24, 0, 9, 17, -2, 3, 8, 48, 11, 76, 10, 12, -1, 5, 9, 49, 102, 74, 9, 12, 0, 5, 9, 50, 48, 63, 10, 12, -1, 5, 9, 51, 59, 63, 10, 12, -1, 5, 9, 52, 70, 63, 10, 12, -1, 5, 9, 53, 81, 62, 10, 12, -1, 5, 9, 54, 235, 59, 10, 12, -1, 5, 9, 55, 103, 61, 10, 12, -1, 5, 9, 56, 114, 60, 10, 12, -1, 5, 9, 57, 125, 60, 10, 12, -1, 5, 9, 58, 252, 45, 3, 9, 0, 8, 5, 59, 246, 71, 4, 11, -1, 8, 5, 60, 148, 73, 8, 12, 0, 5, 9, 61, 0, 100, 8, 7, 0, 8, 9, 62, 139, 73, 8, 12, 0, 5, 9, 63, 40, 49, 8, 13, 0, 4, 8, 64, 184, 17, 15, 14, 0, 5, 16, 65, 178, 47, 12, 12, -1, 5, 11, 66, 136, 60, 10, 12, 0, 5, 10, 67, 240, 45, 11, 12, -1, 5, 10, 68, 0, 63, 11, 12, 0, 5, 11, 69, 130, 73, 8, 12, 0, 5, 9, 70, 112, 74, 8, 12, 0, 5, 8, 71, 191, 46, 12, 12, -1, 5, 11, 72, 24, 63, 11, 12, 0, 5, 11, 73, 193, 72, 4, 12, 0, 5, 5, 74, 173, 73, 6, 12, -1, 5, 6, 75, 147, 60, 10, 12, 0, 5, 10, 76, 121, 73, 8, 12, 0, 5, 8, 77, 122, 47, 15, 12, 0, 5, 16, 78, 12, 63, 11, 12, 0, 5, 12, 79, 138, 47, 13, 12, -1, 5, 12, 80, 62, 76, 9, 12, 0, 5, 10, 81, 200, 17, 14, 14, -1, 5, 12, 82, 158, 60, 10, 12, 0, 5, 10, 83, 32, 76, 9, 12, -1, 5, 9, 84, 169, 60, 10, 12, -1, 5, 9, 85, 204, 46, 11, 12, 0, 5, 12, 86, 165, 47, 12, 12, -1, 5, 11, 87, 88, 48, 17, 12, -1, 5, 16, 88, 216, 46, 11, 12, -1, 5, 10, 89, 228, 46, 11, 12, -1, 5, 9, 90, 180, 60, 10, 12, -1, 5, 9, 91, 249, 0, 5, 16, 0, 4, 6, 92, 13, 0, 10, 17, -2, 3, 8, 93, 85, 17, 5, 16, 0, 4, 6, 94, 196, 85, 10, 8, -1, 5, 9, 95, 71, 99, 10, 3, -1, 17, 9, 96, 46, 99, 6, 4, -1, 4, 5, 97, 111, 87, 9, 9, -1, 8, 9, 98, 207, 32, 9, 13, 0, 4, 10, 99, 161, 86, 8, 9, -1, 8, 8, 100, 69, 34, 10, 13, -1, 4, 10, 101, 89, 88, 10, 9, -1, 8, 9, 102, 58, 49, 7, 13, -1, 4, 6, 103, 191, 59, 10, 12, -1, 8, 9, 104, 177, 33, 9, 13, 0, 4, 10, 105, 187, 73, 5, 12, -1, 5, 4, 106, 177, 17, 6, 15, -2, 5, 5, 107, 217, 32, 9, 13, 0, 4, 9, 108, 250, 17, 4, 13, 0, 4, 4, 109, 24, 89, 14, 9, 0, 8, 15, 110, 151, 86, 9, 9, 0, 8, 10, 111, 100, 87, 10, 9, -1, 8, 10, 112, 42, 76, 9, 12, 0, 8, 10, 113, 213, 59, 10, 12, -1, 8, 10, 114, 188, 86, 7, 9, 0, 8, 6, 115, 170, 86, 8, 9, -1, 8, 7, 116, 230, 72, 7, 11, -1, 6, 6, 117, 141, 86, 9, 9, 0, 8, 10, 118, 78, 89, 10, 9, -1, 8, 9, 119, 8, 89, 15, 9, -1, 8, 13, 120, 131, 86, 9, 9, -1, 8, 8, 121, 224, 59, 10, 12, -1, 8, 9, 122, 179, 86, 8, 9, -1, 8, 7, 123, 34, 0, 6, 17, -1, 3, 6, 124, 64, 0, 3, 17, 2, 3, 9, 125, 41, 0, 6, 17, 0, 3, 6, 126, 30, 99, 9, 5, 0, 5, 9, 160, 114, 97, 2, 2, -1, 16, 4, 161, 80, 48, 3, 13, 1, 6, 6, 162, 165, 73, 7, 12, 0, 5, 9, 163, 92, 74, 9, 12, 0, 5, 9, 164, 209, 72, 10, 11, -1, 6, 9, 165, 36, 63, 11, 12, -2, 5, 9, 166, 60, 0, 3, 17, 2, 3, 9, 167, 151, 17, 8, 15, 0, 4, 9, 168, 82, 99, 6, 3, 0, 5, 7, 169, 236, 17, 13, 13, 0, 4, 15, 170, 238, 72, 7, 11, 0, 5, 7, 171, 54, 89, 11, 9, -1, 7, 10, 172, 20, 99, 9, 5, -1, 10, 9, 173, 96, 98, 5, 3, 0, 10, 6, 174, 226, 84, 8, 8, 0, 4, 9, 175, 89, 98, 6, 3, 0, 4, 7, 176, 40, 99, 5, 5, 0, 4, 6, 177, 22, 76, 9, 12, 0, 5, 9, 178, 235, 84, 6, 8, 0, 3, 6, 179, 242, 84, 6, 8, 0, 3, 6, 180, 53, 99, 6, 4, -1, 4, 5, 181, 0, 76, 10, 12, 0, 8, 10, 182, 215, 17, 10, 14, 0, 5, 11, 183, 251, 78, 3, 4, 0, 9, 5, 184, 60, 99, 5, 4, 0, 16, 5, 185, 249, 83, 5, 8, -1, 3, 5, 186, 0, 89, 7, 10, 0, 5, 8, 187, 66, 89, 11, 9, -1, 7, 10, 188, 42, 35, 13, 13, -1, 4, 12, 189, 0, 35, 13, 13, -1, 4, 12, 190, 14, 35, 13, 13, -1, 4, 13, 191, 49, 49, 8, 13, -1, 6, 8, 192, 150, 0, 12, 16, -1, 1, 11, 193, 176, 0, 12, 16, -1, 1, 11, 194, 163, 0, 12, 16, -1, 1, 11, 195, 124, 0, 12, 16, -1, 1, 11, 196, 105, 17, 12, 15, -1, 2, 11, 197, 0, 0, 12, 17, -1, 0, 11, 198, 106, 47, 15, 12, -1, 5, 14, 199, 141, 17, 9, 15, 0, 5, 10, 200, 60, 18, 8, 16, 0, 1, 9, 201, 41, 18, 9, 16, 0, 1, 9, 202, 21, 18, 9, 16, 0, 1, 9, 203, 160, 17, 8, 15, 0, 2, 9, 204, 69, 17, 7, 16, -2, 1, 5, 205, 77, 17, 7, 16, -1, 1, 5, 206, 51, 18, 8, 16, -2, 1, 5, 207, 169, 17, 7, 15, -2, 2, 5, 208, 152, 47, 12, 12, -1, 5, 12, 209, 237, 0, 11, 16, 0, 1, 12, 210, 82, 0, 13, 16, -1, 1, 12, 211, 68, 0, 13, 16, -1, 1, 12, 212, 110, 0, 13, 16, -1, 1, 12, 213, 96, 0, 13, 16, -1, 1, 12, 214, 91, 17, 13, 15, -1, 2, 12, 215, 217, 84, 8, 8, 0, 8, 9, 216, 137, 0, 12, 16, 0, 3, 12, 217, 225, 0, 11, 16, 0, 1, 12, 218, 189, 0, 11, 16, 0, 1, 12, 219, 213, 0, 11, 16, 0, 1, 12, 220, 118, 17, 11, 15, 0, 2, 12, 221, 201, 0, 11, 16, -1, 1, 9, 222, 72, 76, 9, 12, 0, 5, 10, 223, 167, 33, 9, 13, 0, 4, 10, 224, 157, 33, 9, 13, -1, 4, 9, 225, 30, 49, 9, 13, -1, 4, 9, 226, 227, 32, 9, 13, -1, 4, 9, 227, 237, 31, 9, 13, -1, 4, 9, 228, 82, 75, 9, 12, -1, 5, 9, 229, 226, 17, 9, 14, -1, 3, 9, 230, 39, 89, 14, 9, 0, 8, 14, 231, 157, 73, 7, 12, 0, 8, 8, 232, 146, 33, 10, 13, -1, 4, 9, 233, 80, 34, 10, 13, -1, 4, 9, 234, 91, 33, 10, 13, -1, 4, 9, 235, 92, 61, 10, 12, -1, 5, 9, 236, 66, 49, 6, 13, -2, 4, 4, 237, 73, 48, 6, 13, -1, 4, 4, 238, 247, 31, 8, 13, -2, 4, 4, 239, 180, 73, 6, 12, -1, 5, 4, 240, 187, 32, 9, 13, 0, 4, 10, 241, 197, 32, 9, 13, 0, 4, 10, 242, 102, 33, 10, 13, -1, 4, 10, 243, 113, 33, 10, 13, -1, 4, 10, 244, 124, 33, 10, 13, -1, 4, 10, 245, 135, 33, 10, 13, -1, 4, 10, 246, 202, 59, 10, 12, -1, 5, 10, 247, 220, 72, 9, 11, -1, 6, 9, 248, 198, 72, 10, 11, 0, 7, 10, 249, 0, 49, 9, 13, 0, 4, 10, 250, 10, 49, 9, 13, 0, 4, 10, 251, 20, 49, 9, 13, 0, 4, 10, 252, 246, 58, 9, 12, 0, 5, 10, 253, 0, 18, 10, 16, -1, 4, 9, 254, 11, 18, 9, 16, 0, 4, 10, 255, 130, 17, 10, 15, -1, 5, 9};
diff --git a/tools/editor/editor_data.cpp b/tools/editor/editor_data.cpp
index aeca76bb29..673ee30adb 100644
--- a/tools/editor/editor_data.cpp
+++ b/tools/editor/editor_data.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -39,7 +39,7 @@ void EditorHistory::_cleanup_history() {
bool fail=false;
for(int j=0;j<history[i].path.size();j++) {
- if (!history[i].path[j].res.is_null())
+ if (!history[i].path[j].ref.is_null())
continue;
if (ObjectDB::get_instance(history[i].path[j].object))
@@ -70,10 +70,10 @@ void EditorHistory::_add_object(ObjectID p_object,const String& p_property,int p
Object *obj = ObjectDB::get_instance(p_object);
ERR_FAIL_COND(!obj);
- Resource *r = obj->cast_to<Resource>();
+ Reference *r = obj->cast_to<Reference>();
Obj o;
if (r)
- o.res=RES(r);
+ o.ref=REF(r);
o.object=p_object;
o.property=p_property;
@@ -123,6 +123,27 @@ void EditorHistory::add_object(ObjectID p_object,int p_relevel){
_add_object(p_object,"",p_relevel);
}
+int EditorHistory::get_history_len() {
+ return history.size();
+}
+int EditorHistory::get_history_pos() {
+ return current;
+}
+
+ObjectID EditorHistory::get_history_obj(int p_obj) const {
+ ERR_FAIL_INDEX_V(p_obj,history.size(),0);
+ ERR_FAIL_INDEX_V(history[p_obj].level,history[p_obj].path.size(),0);
+ return history[p_obj].path[history[p_obj].level].object;
+}
+
+bool EditorHistory::is_at_begining() const {
+ return current<=0;
+}
+bool EditorHistory::is_at_end() const {
+
+ return ((current+1)>=history.size());
+}
+
bool EditorHistory::next() {
@@ -424,9 +445,225 @@ void EditorData::remove_custom_type(const String& p_type){
}
+int EditorData::add_edited_scene(int p_at_pos) {
+
+ if (p_at_pos<0)
+ p_at_pos=edited_scene.size();
+ EditedScene es;
+ es.root=NULL;
+ es.history_current=-1;
+ es.version=0;
+ es.live_edit_root=NodePath(String("/root"));
+
+ if (p_at_pos==edited_scene.size())
+ edited_scene.push_back(es);
+ else
+ edited_scene.insert(p_at_pos,es);
+
+ if (current_edited_scene<0)
+ current_edited_scene=0;
+ return p_at_pos;
+}
+
+void EditorData::move_edited_scene_index(int p_idx,int p_to_idx){
+
+ ERR_FAIL_INDEX(p_idx,edited_scene.size());
+ ERR_FAIL_INDEX(p_to_idx,edited_scene.size());
+ SWAP(edited_scene[p_idx],edited_scene[p_to_idx]);
+}
+void EditorData::remove_scene(int p_idx){
+ ERR_FAIL_INDEX(p_idx,edited_scene.size());
+ if (edited_scene[p_idx].root)
+ memdelete(edited_scene[p_idx].root);
+
+ if (current_edited_scene>p_idx)
+ current_edited_scene--;
+ else if (current_edited_scene==p_idx && current_edited_scene>0) {
+ current_edited_scene--;
+ }
+
+ edited_scene.remove(p_idx);
+
+}
+int EditorData::get_edited_scene() const {
+
+ return current_edited_scene;
+}
+void EditorData::set_edited_scene(int p_idx){
+
+ ERR_FAIL_INDEX(p_idx,edited_scene.size());
+ current_edited_scene=p_idx;
+ //swap
+}
+Node* EditorData::get_edited_scene_root(){
+
+ ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),NULL);
+
+ return edited_scene[current_edited_scene].root;
+}
+void EditorData::set_edited_scene_root(Node* p_root) {
+
+ ERR_FAIL_INDEX(current_edited_scene,edited_scene.size());
+ edited_scene[current_edited_scene].root=p_root;
+}
+
+int EditorData::get_edited_scene_count() const {
+
+ return edited_scene.size();
+}
+
+void EditorData::set_edited_scene_version(uint64_t version) {
+ ERR_FAIL_INDEX(current_edited_scene,edited_scene.size());
+ edited_scene[current_edited_scene].version=version;
+
+}
+
+uint64_t EditorData::get_edited_scene_version() const{
+
+ ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),0);
+ return edited_scene[current_edited_scene].version;
+
+}
+uint64_t EditorData::get_scene_version(int p_idx) const{
+ ERR_FAIL_INDEX_V(p_idx,edited_scene.size(),false);
+ return edited_scene[p_idx].version;
+}
+
+String EditorData::get_scene_type(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,edited_scene.size(),String());
+ if (!edited_scene[p_idx].root)
+ return "";
+ return edited_scene[p_idx].root->get_type();
+
+}
+
+Ref<Script> EditorData::get_scene_root_script(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,edited_scene.size(),Ref<Script>());
+ if (!edited_scene[p_idx].root)
+ return Ref<Script>();
+ Ref<Script> s=edited_scene[p_idx].root->get_script();
+ if (!s.is_valid() && edited_scene[p_idx].root->get_child_count()) {
+ Node *n = edited_scene[p_idx].root->get_child(0);
+ while(!s.is_valid() && n && n->get_filename()==String()) {
+ s=n->get_script();
+ n=n->get_parent();
+ }
+ }
+ return s;
+}
+
+String EditorData::get_scene_title(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx,edited_scene.size(),String());
+ if (!edited_scene[p_idx].root)
+ return "[empty]";
+ if (edited_scene[p_idx].root->get_filename()=="")
+ return "[unsaved]";
+ return edited_scene[p_idx].root->get_filename().get_file();
+}
+
+
+String EditorData::get_scene_path(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,edited_scene.size(),String());
+
+ if (!edited_scene[p_idx].root)
+ return "";
+ return edited_scene[p_idx].root->get_filename();
+
+}
+
+void EditorData::set_edited_scene_live_edit_root(const NodePath& p_root) {
+ ERR_FAIL_INDEX(current_edited_scene,edited_scene.size());
+
+ edited_scene[current_edited_scene].live_edit_root=p_root;
+
+}
+NodePath EditorData::get_edited_scene_live_edit_root() {
+
+ ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),String());
+
+ return edited_scene[current_edited_scene].live_edit_root;
+
+
+
+}
+
+
+void EditorData::save_edited_scene_state(EditorSelection *p_selection, EditorHistory *p_history, const Dictionary& p_custom) {
+
+ ERR_FAIL_INDEX(current_edited_scene,edited_scene.size());
+
+ EditedScene &es=edited_scene[current_edited_scene];
+ es.selection = p_selection->get_selected_node_list();
+ es.history_current=p_history->current;
+ es.history_stored=p_history->history;
+ es.editor_states=get_editor_states();
+ es.custom_state=p_custom;
+
+}
+
+Dictionary EditorData::restore_edited_scene_state(EditorSelection *p_selection, EditorHistory *p_history) {
+ ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),Dictionary());
+
+ EditedScene &es=edited_scene[current_edited_scene];
+
+ p_history->current=es.history_current;
+ p_history->history=es.history_stored;
+
+
+ p_selection->clear();
+ for(List<Node*>::Element *E=es.selection.front();E;E=E->next()) {
+ p_selection->add_node(E->get());
+ }
+ set_editor_states(es.editor_states);
+
+ return es.custom_state;
+}
+
+
+void EditorData::set_edited_scene_import_metadata(Ref<ResourceImportMetadata> p_mdata) {
+
+ ERR_FAIL_INDEX(current_edited_scene,edited_scene.size());
+ edited_scene[current_edited_scene].medatata=p_mdata;
+
+}
+
+Ref<ResourceImportMetadata> EditorData::get_edited_scene_import_metadata() const{
+
+ ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),Ref<ResourceImportMetadata>());
+ return edited_scene[current_edited_scene].medatata;
+}
+
+void EditorData::clear_edited_scenes() {
+
+ for(int i=0;i<edited_scene.size();i++) {
+ if (edited_scene[i].root) {
+ memdelete( edited_scene[i].root );
+ }
+ }
+ edited_scene.clear();
+}
+
+
+
+void EditorData::set_plugin_window_layout(Ref<ConfigFile> p_layout) {
+ for(int i=0;i<editor_plugins.size();i++) {
+ editor_plugins[i]->set_window_layout(p_layout);
+ }
+}
+
+void EditorData::get_plugin_window_layout(Ref<ConfigFile> p_layout) {
+ for(int i=0;i<editor_plugins.size();i++) {
+ editor_plugins[i]->get_window_layout(p_layout);
+ }
+}
EditorData::EditorData() {
+ current_edited_scene=-1;
+
// load_imported_scenes_from_globals();
}
diff --git a/tools/editor/editor_data.h b/tools/editor/editor_data.h
index 50faba88ee..c5ee83ae63 100644
--- a/tools/editor/editor_data.h
+++ b/tools/editor/editor_data.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -45,7 +45,7 @@ class EditorHistory {
struct Obj {
- RES res;
+ REF ref;
ObjectID object;
String property;
};
@@ -55,6 +55,7 @@ class EditorHistory {
Vector<Obj> path;
int level;
};
+friend class EditorData;
Vector<History> history;
int current;
@@ -74,10 +75,17 @@ class EditorHistory {
public:
+ bool is_at_begining() const;
+ bool is_at_end() const;
+
void add_object(ObjectID p_object);
void add_object(ObjectID p_object,const String& p_subprop);
void add_object(ObjectID p_object,int p_relevel);
+ int get_history_len();
+ int get_history_pos();
+ ObjectID get_history_obj(int p_obj) const;
+
bool next();
bool previous();
ObjectID get_current();
@@ -91,6 +99,8 @@ public:
EditorHistory();
};
+class EditorSelection;
+
class EditorData {
public:
@@ -117,6 +127,22 @@ private:
void _cleanup_history();
+ struct EditedScene {
+ Node* root;
+ Dictionary editor_states;
+ Ref<ResourceImportMetadata> medatata;
+ List<Node*> selection;
+ Vector<EditorHistory::History> history_stored;
+ int history_current;
+ Dictionary custom_state;
+ uint64_t version;
+ NodePath live_edit_root;
+
+
+ };
+
+ Vector<EditedScene> edited_scene;
+ int current_edited_scene;
public:
@@ -146,6 +172,36 @@ public:
void remove_custom_type(const String& p_type);
const Map<String,Vector<CustomType> >& get_custom_types() const { return custom_types; }
+
+ int add_edited_scene(int p_at_pos);
+ void move_edited_scene_index(int p_idx,int p_to_idx);
+ void remove_scene(int p_idx);
+ void set_edited_scene(int p_idx);
+ void set_edited_scene_root(Node* p_root);
+ void set_edited_scene_import_metadata(Ref<ResourceImportMetadata> p_mdata);
+ Ref<ResourceImportMetadata> get_edited_scene_import_metadata() const;
+ int get_edited_scene() const;
+ Node* get_edited_scene_root();
+ int get_edited_scene_count() const;
+ String get_scene_title(int p_idx) const;
+ String get_scene_path(int p_idx) const;
+ String get_scene_type(int p_idx) const;
+ Ref<Script> get_scene_root_script(int p_idx) const;
+ void set_edited_scene_version(uint64_t version);
+ uint64_t get_edited_scene_version() const;
+ uint64_t get_scene_version(int p_idx) const;
+ void clear_edited_scenes();
+ void set_edited_scene_live_edit_root(const NodePath& p_root);
+ NodePath get_edited_scene_live_edit_root();
+
+
+ void set_plugin_window_layout(Ref<ConfigFile> p_layout);
+ void get_plugin_window_layout(Ref<ConfigFile> p_layout);
+
+ void save_edited_scene_state(EditorSelection *p_selection,EditorHistory *p_history,const Dictionary& p_custom);
+ Dictionary restore_edited_scene_state(EditorSelection *p_selection, EditorHistory *p_history);
+
+
EditorData();
};
diff --git a/tools/editor/editor_dir_dialog.cpp b/tools/editor/editor_dir_dialog.cpp
index eee4125e93..a8421acff8 100644
--- a/tools/editor/editor_dir_dialog.cpp
+++ b/tools/editor/editor_dir_dialog.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,6 +28,9 @@
/*************************************************************************/
#include "editor_dir_dialog.h"
#include "os/os.h"
+#include "os/keyboard.h"
+#include "tools/editor/editor_settings.h"
+
void EditorDirDialog::_update_dir(TreeItem* p_item) {
@@ -39,17 +42,33 @@ void EditorDirDialog::_update_dir(TreeItem* p_item) {
da->change_dir(cdir);
da->list_dir_begin();
String p=da->get_next();
+
+ List<String> dirs;
+ bool ishidden;
+ bool show_hidden = EditorSettings::get_singleton()->get("file_dialog/show_hidden_files");
+
while(p!="") {
- if (da->current_is_dir() && !p.begins_with(".")) {
- TreeItem *ti = tree->create_item(p_item);
- ti->set_text(0,p);
- ti->set_icon(0,get_icon("Folder","EditorIcons"));
- ti->set_collapsed(true);
- }
+ ishidden = da->current_is_hidden();
+
+ if (show_hidden || !ishidden) {
+ if (da->current_is_dir() && !p.begins_with(".")) {
+ dirs.push_back(p);
+ }
+ }
p=da->get_next();
}
+ dirs.sort();
+
+ for(List<String>::Element *E=dirs.front();E;E=E->next()) {
+ TreeItem *ti = tree->create_item(p_item);
+ ti->set_text(0,E->get());
+ ti->set_icon(0,get_icon("Folder","EditorIcons"));
+ ti->set_collapsed(true);
+
+ }
+
memdelete(da);
updating=false;
diff --git a/tools/editor/editor_dir_dialog.h b/tools/editor/editor_dir_dialog.h
index 060fb724ce..8ac83b86e8 100644
--- a/tools/editor/editor_dir_dialog.h
+++ b/tools/editor/editor_dir_dialog.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/editor_file_dialog.cpp b/tools/editor/editor_file_dialog.cpp
new file mode 100644
index 0000000000..c62347d129
--- /dev/null
+++ b/tools/editor/editor_file_dialog.cpp
@@ -0,0 +1,1367 @@
+#include "editor_file_dialog.h"
+#include "scene/gui/label.h"
+#include "scene/gui/center_container.h"
+#include "print_string.h"
+#include "os/keyboard.h"
+#include "editor_resource_preview.h"
+#include "editor_settings.h"
+#include "scene/gui/margin_container.h"
+EditorFileDialog::GetIconFunc EditorFileDialog::get_icon_func=NULL;
+EditorFileDialog::GetIconFunc EditorFileDialog::get_large_icon_func=NULL;
+
+EditorFileDialog::RegisterFunc EditorFileDialog::register_func=NULL;
+EditorFileDialog::RegisterFunc EditorFileDialog::unregister_func=NULL;
+
+
+VBoxContainer *EditorFileDialog::get_vbox() {
+ return vbox;
+
+}
+
+void EditorFileDialog::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+
+ mode_thumbnails->set_icon(get_icon("FileThumbnail","EditorIcons"));
+ mode_list->set_icon(get_icon("FileList","EditorIcons"));
+ dir_prev->set_icon(get_icon("ArrowLeft","EditorIcons"));
+ dir_next->set_icon(get_icon("ArrowRight","EditorIcons"));
+ dir_up->set_icon(get_icon("ArrowUp","EditorIcons"));
+ favorite->set_icon(get_icon("Favorites","EditorIcons"));
+
+ fav_up->set_icon(get_icon("MoveUp","EditorIcons"));
+ fav_down->set_icon(get_icon("MoveDown","EditorIcons"));
+ fav_rm->set_icon(get_icon("RemoveSmall","EditorIcons"));
+
+ }
+ if (p_what==NOTIFICATION_PROCESS) {
+
+ if (preview_waiting) {
+ preview_wheel_timeout-=get_process_delta_time();
+ if (preview_wheel_timeout<=0) {
+ preview_wheel_index++;
+ if (preview_wheel_index>=8)
+ preview_wheel_index=0;
+ Ref<Texture> frame = get_icon("WaitPreview"+itos(preview_wheel_index+1),"EditorIcons");
+ preview->set_texture(frame);
+ preview_wheel_timeout=0.1;
+ }
+ }
+ }
+
+ if (p_what==NOTIFICATION_DRAW) {
+
+ //RID ci = get_canvas_item();
+ //get_stylebox("panel","PopupMenu")->draw(ci,Rect2(Point2(),get_size()));
+ }
+}
+
+void EditorFileDialog::set_enable_multiple_selection(bool p_enable) {
+
+ item_list->set_select_mode(p_enable?ItemList::SELECT_MULTI:ItemList::SELECT_SINGLE);
+
+};
+
+Vector<String> EditorFileDialog::get_selected_files() const {
+
+ Vector<String> list;
+ for(int i=0;i<item_list->get_item_count();i++) {
+ if (item_list->is_selected(i))
+ list.push_back(item_list->get_item_text(i));
+ }
+ return list;
+
+};
+
+void EditorFileDialog::update_dir() {
+
+ dir->set_text(dir_access->get_current_dir());
+}
+
+void EditorFileDialog::_dir_entered(String p_dir) {
+
+
+ dir_access->change_dir(p_dir);
+ file->set_text("");
+ invalidate();
+ update_dir();
+ _push_history();
+
+
+}
+
+void EditorFileDialog::_file_entered(const String& p_file) {
+
+ _action_pressed();
+}
+
+void EditorFileDialog::_save_confirm_pressed() {
+ String f=dir_access->get_current_dir().plus_file(file->get_text());
+ _save_to_recent();
+ emit_signal("file_selected",f);
+ hide();
+}
+
+void EditorFileDialog::_post_popup() {
+
+ ConfirmationDialog::_post_popup();
+ if (invalidated) {
+ update_file_list();
+ invalidated=false;
+ }
+ if (mode==MODE_SAVE_FILE)
+ file->grab_focus();
+ else
+ item_list->grab_focus();
+
+ if (is_visible() && get_current_file()!="")
+ _request_single_thumbnail(get_current_dir().plus_file(get_current_file()));
+
+ if (is_visible()) {
+ Ref<Texture> folder = get_icon("folder","FileDialog");
+ recent->clear();
+
+
+ bool res = access==ACCESS_RESOURCES;
+ Vector<String> recentd = EditorSettings::get_singleton()->get_recent_dirs();
+ for(int i=0;i<recentd.size();i++) {
+ bool cres = recentd[i].begins_with("res://");
+ if (cres!=res)
+ continue;
+ String name = recentd[i];
+ if (res && name=="res://") {
+ name="/";
+ } else {
+ name=name.get_file()+"/";
+ }
+
+ //print_line("file: "+name);
+ recent->add_item(name,folder);
+ recent->set_item_metadata( recent->get_item_count()-1,recentd[i]);
+ }
+
+ local_history.clear();
+ local_history_pos=-1;
+ _push_history();
+
+ _update_favorites();
+ }
+
+}
+
+void EditorFileDialog::_thumbnail_result(const String& p_path,const Ref<Texture>& p_preview, const Variant& p_udata) {
+
+ if (display_mode==DISPLAY_LIST || p_preview.is_null())
+ return;
+
+ for(int i=0;i<item_list->get_item_count();i++) {
+ Dictionary d = item_list->get_item_metadata(i);
+ String pname = d["path"];
+ if (pname==p_path) {
+ item_list->set_item_icon(i,p_preview);
+ item_list->set_item_tag_icon(i,Ref<Texture>());
+ }
+ }
+}
+
+void EditorFileDialog::_thumbnail_done(const String& p_path,const Ref<Texture>& p_preview, const Variant& p_udata) {
+
+ set_process(false);
+ preview_waiting=false;
+
+ if (p_preview.is_valid() && get_current_path()==p_path) {
+
+
+ preview->set_texture(p_preview);
+ if (display_mode==DISPLAY_THUMBNAILS) {
+ preview_vb->hide();
+ } else {
+ preview_vb->show();
+ }
+
+ } else {
+ preview_vb->hide();
+ preview->set_texture(Ref<Texture>());
+
+ }
+
+}
+
+void EditorFileDialog::_request_single_thumbnail(const String& p_path) {
+
+ EditorResourcePreview::get_singleton()->queue_resource_preview(p_path,this,"_thumbnail_done",p_path);
+ //print_line("want file "+p_path);
+ set_process(true);
+ preview_waiting=true;
+ preview_wheel_timeout=0;
+
+}
+
+void EditorFileDialog::_action_pressed() {
+
+ if (mode==MODE_OPEN_FILES) {
+
+
+ String fbase=dir_access->get_current_dir();
+
+ DVector<String> files;
+ for(int i=0;i<item_list->get_item_count();i++) {
+ if (item_list->is_selected(i))
+ files.push_back( fbase.plus_file(item_list->get_item_text(i) ));
+ }
+
+ if (files.size()) {
+ _save_to_recent();
+ emit_signal("files_selected",files);
+ hide();
+ }
+
+ return;
+ }
+
+ String f=dir_access->get_current_dir().plus_file(file->get_text());
+
+ if (mode==MODE_OPEN_FILE && dir_access->file_exists(f)) {
+ _save_to_recent();
+ emit_signal("file_selected",f);
+ hide();
+ }
+
+ if (mode==MODE_OPEN_DIR) {
+
+
+ String path=dir_access->get_current_dir();
+ /*if (tree->get_selected()) {
+ Dictionary d = tree->get_selected()->get_metadata(0);
+ if (d["dir"]) {
+ path=path+"/"+String(d["name"]);
+ }
+ }*/
+ path=path.replace("\\","/");
+ _save_to_recent();
+ emit_signal("dir_selected",path);
+ hide();
+ }
+
+ if (mode==MODE_SAVE_FILE) {
+
+ bool valid=false;
+
+ if (filter->get_selected()==filter->get_item_count()-1) {
+ valid=true; //match none
+ } else if (filters.size()>1 && filter->get_selected()==0) {
+ // match all filters
+ for (int i=0;i<filters.size();i++) {
+
+ String flt=filters[i].get_slice(";",0);
+ for (int j=0;j<flt.get_slice_count(",");j++) {
+
+ String str = flt.get_slice(",",j).strip_edges();
+ if (f.match(str)) {
+ valid=true;
+ break;
+ }
+ }
+ if (valid)
+ break;
+ }
+ } else {
+ int idx=filter->get_selected();
+ if (filters.size()>1)
+ idx--;
+ if (idx>=0 && idx<filters.size()) {
+
+ String flt=filters[idx].get_slice(";",0);
+ int filterSliceCount=flt.get_slice_count(",");
+ for (int j=0;j<filterSliceCount;j++) {
+
+ String str = (flt.get_slice(",",j).strip_edges());
+ if (f.match(str)) {
+ valid=true;
+ break;
+ }
+ }
+
+ if (!valid && filterSliceCount>0) {
+ String str = (flt.get_slice(",",0).strip_edges());
+ f+=str.substr(1, str.length()-1);
+ _request_single_thumbnail(get_current_dir().plus_file(f.get_file()));
+ file->set_text(f.get_file());
+ valid=true;
+ }
+ } else {
+ valid=true;
+ }
+ }
+
+
+ if (!valid) {
+
+ exterr->popup_centered_minsize(Size2(250,80));
+ return;
+
+ }
+
+ if (dir_access->file_exists(f)) {
+ confirm_save->set_text("File Exists, Overwrite?");
+ confirm_save->popup_centered(Size2(200,80));
+ } else {
+
+ _save_to_recent();
+ emit_signal("file_selected",f);
+ hide();
+ }
+ }
+}
+
+void EditorFileDialog::_cancel_pressed() {
+
+ file->set_text("");
+ invalidate();
+ hide();
+}
+
+void EditorFileDialog::_item_selected(int p_item) {
+
+ int current = p_item;
+ if (current<0 || current>=item_list->get_item_count())
+ return;
+
+ Dictionary d=item_list->get_item_metadata(current);
+
+ if (!d["dir"]) {
+
+ file->set_text(d["name"]);
+ _request_single_thumbnail(get_current_dir().plus_file(get_current_file()));
+ }
+}
+
+void EditorFileDialog::_push_history() {
+
+ local_history.resize(local_history_pos+1);
+ String new_path = dir_access->get_current_dir();
+ if (local_history.size()==0 || new_path!=local_history[local_history_pos]) {
+ local_history.push_back(new_path);
+ local_history_pos++;
+ dir_prev->set_disabled(local_history_pos==0);
+ dir_next->set_disabled(true);
+ }
+
+}
+void EditorFileDialog::_item_dc_selected(int p_item) {
+
+
+ int current = p_item;
+ if (current<0 || current>=item_list->get_item_count())
+ return;
+
+ Dictionary d=item_list->get_item_metadata(current);
+
+ if (d["dir"]) {
+
+ //print_line("change dir: "+String(d["name"]));
+ dir_access->change_dir(d["name"]);
+ if (mode==MODE_OPEN_FILE || mode==MODE_OPEN_FILES || mode==MODE_OPEN_DIR)
+ file->set_text("");
+ call_deferred("_update_file_list");
+ call_deferred("_update_dir");
+
+ _push_history();
+
+
+ } else {
+
+ _action_pressed();
+ }
+}
+
+
+
+void EditorFileDialog::update_file_list() {
+
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+ Ref<Texture> folder_thumbnail;
+ Ref<Texture> file_thumbnail;
+
+ item_list->clear();
+
+ if (display_mode==DISPLAY_THUMBNAILS) {
+
+ item_list->set_max_columns(0);
+ item_list->set_icon_mode(ItemList::ICON_MODE_TOP);
+ item_list->set_fixed_column_width(thumbnail_size*3/2);
+ item_list->set_max_text_lines(2);
+ item_list->set_min_icon_size(Size2(thumbnail_size,thumbnail_size));
+
+ if (!has_icon("ResizedFolder","EditorIcons")) {
+ Ref<ImageTexture> folder = get_icon("FolderBig","EditorIcons");
+ Image img = folder->get_data();
+ img.resize(thumbnail_size,thumbnail_size);
+ Ref<ImageTexture> resized_folder = Ref<ImageTexture>( memnew( ImageTexture));
+ resized_folder->create_from_image(img,0);
+ Theme::get_default()->set_icon("ResizedFolder","EditorIcons",resized_folder);
+ }
+
+ folder_thumbnail = get_icon("ResizedFolder","EditorIcons");
+
+ if (!has_icon("ResizedFile","EditorIcons")) {
+ Ref<ImageTexture> file = get_icon("FileBig","EditorIcons");
+ Image img = file->get_data();
+ img.resize(thumbnail_size,thumbnail_size);
+ Ref<ImageTexture> resized_file = Ref<ImageTexture>( memnew( ImageTexture));
+ resized_file->create_from_image(img,0);
+ Theme::get_default()->set_icon("ResizedFile","EditorIcons",resized_file);
+ }
+
+ file_thumbnail = get_icon("ResizedFile","EditorIcons");
+
+ preview_vb->hide();
+
+ } else {
+
+ item_list->set_icon_mode(ItemList::ICON_MODE_LEFT);
+ item_list->set_max_columns(1);
+ item_list->set_max_text_lines(1);
+ item_list->set_fixed_column_width(0);
+ item_list->set_min_icon_size(Size2());
+ if (preview->get_texture().is_valid())
+ preview_vb->show();
+
+ }
+
+
+ dir_access->list_dir_begin();
+
+
+ Ref<Texture> folder = get_icon("folder","FileDialog");
+ List<String> files;
+ List<String> dirs;
+
+ bool isdir;
+ bool ishidden;
+ bool show_hidden = show_hidden_files;
+ String item;
+
+ while ((item=dir_access->get_next(&isdir))!="") {
+
+ ishidden = dir_access->current_is_hidden();
+
+ if (show_hidden || !ishidden) {
+ if (!isdir)
+ files.push_back(item);
+ else
+ dirs.push_back(item);
+ }
+ }
+
+ dirs.sort_custom<NoCaseComparator>();
+ files.sort_custom<NoCaseComparator>();
+
+ while(!dirs.empty()) {
+
+ if (dirs.front()->get()!=".") {
+ item_list->add_item(dirs.front()->get()+"/");
+ if (display_mode==DISPLAY_THUMBNAILS) {
+
+ item_list->set_item_icon(item_list->get_item_count()-1,folder_thumbnail);
+ } else {
+
+ item_list->set_item_icon(item_list->get_item_count()-1,folder);
+ }
+
+ Dictionary d;
+ d["name"]=dirs.front()->get();
+ d["path"]=String();
+ d["dir"]=true;
+ item_list->set_item_metadata( item_list->get_item_count() -1, d);
+ }
+ dirs.pop_front();
+
+ }
+
+ dirs.clear();
+
+ List<String> patterns;
+ // build filter
+ if (filter->get_selected()==filter->get_item_count()-1) {
+
+ // match all
+ } else if (filters.size()>1 && filter->get_selected()==0) {
+ // match all filters
+ for (int i=0;i<filters.size();i++) {
+
+ String f=filters[i].get_slice(";",0);
+ for (int j=0;j<f.get_slice_count(",");j++) {
+
+ patterns.push_back(f.get_slice(",",j).strip_edges());
+ }
+ }
+ } else {
+ int idx=filter->get_selected();
+ if (filters.size()>1)
+ idx--;
+
+ if (idx>=0 && idx<filters.size()) {
+
+ String f=filters[idx].get_slice(";",0);
+ for (int j=0;j<f.get_slice_count(",");j++) {
+
+ patterns.push_back(f.get_slice(",",j).strip_edges());
+ }
+ }
+ }
+
+
+ String base_dir = dir_access->get_current_dir();
+
+
+ while(!files.empty()) {
+
+ bool match=patterns.empty();
+
+ for(List<String>::Element *E=patterns.front();E;E=E->next()) {
+
+ if (files.front()->get().matchn(E->get())) {
+
+ match=true;
+ break;
+ }
+ }
+
+ if (match) {
+ //TreeItem *ti=tree->create_item(root);
+ //ti->set_text(0,files.front()->get());
+ item_list->add_item(files.front()->get());
+
+ if (get_icon_func) {
+
+
+ Ref<Texture> icon = get_icon_func(base_dir.plus_file(files.front()->get()));
+ //ti->set_icon(0,icon);
+ if (display_mode==DISPLAY_THUMBNAILS) {
+
+ item_list->set_item_icon(item_list->get_item_count()-1,file_thumbnail);
+ item_list->set_item_tag_icon(item_list->get_item_count()-1,icon);
+ } else {
+ item_list->set_item_icon(item_list->get_item_count()-1,icon);
+ }
+ }
+
+ if (mode==MODE_OPEN_DIR) {
+ //disabled mode?
+ //ti->set_custom_color(0,get_color("files_disabled"));
+ //ti->set_selectable(0,false);
+ }
+ Dictionary d;
+ d["name"]=files.front()->get();
+ d["dir"]=false;
+ String fullpath = base_dir.plus_file(files.front()->get());
+
+ if (display_mode==DISPLAY_THUMBNAILS) {
+ EditorResourcePreview::get_singleton()->queue_resource_preview(fullpath,this,"_thumbnail_result",fullpath);
+ }
+ d["path"]=base_dir.plus_file(files.front()->get());
+ //ti->set_metadata(0,d);
+ item_list->set_item_metadata(item_list->get_item_count()-1,d);
+
+ if (file->get_text()==files.front()->get())
+ item_list->set_current(item_list->get_item_count()-1);
+ }
+
+ files.pop_front();
+ }
+
+ if (favorites->get_current()>=0) {
+ favorites->unselect(favorites->get_current());
+ }
+
+ favorite->set_pressed(false);
+ fav_up->set_disabled(true);
+ fav_down->set_disabled(true);
+ for(int i=0;i<favorites->get_item_count();i++) {
+ if (favorites->get_item_metadata(i)==base_dir) {
+ favorites->select(i);
+ favorite->set_pressed(true);
+ if (i>0) {
+ fav_up->set_disabled(false);
+ }
+ if (i<favorites->get_item_count()-1) {
+ fav_down->set_disabled(false);
+ }
+ break;
+ }
+
+ }
+ // ??
+ //if (tree->get_root() && tree->get_root()->get_children())
+ // tree->get_root()->get_children()->select(0);
+
+ files.clear();
+
+}
+
+void EditorFileDialog::_filter_selected(int) {
+
+ update_file_list();
+}
+
+void EditorFileDialog::update_filters() {
+
+ filter->clear();
+
+ if (filters.size()>1) {
+ String all_filters;
+
+ const int max_filters=5;
+
+ for(int i=0;i<MIN( max_filters, filters.size()) ;i++) {
+ String flt=filters[i].get_slice(";",0);
+ if (i>0)
+ all_filters+=",";
+ all_filters+=flt;
+ }
+
+ if (max_filters<filters.size())
+ all_filters+=", ...";
+
+ filter->add_item("All Recognized ( "+all_filters+" )");
+ }
+ for(int i=0;i<filters.size();i++) {
+
+ String flt=filters[i].get_slice(";",0).strip_edges();
+ String desc=filters[i].get_slice(";",1).strip_edges();
+ if (desc.length())
+ filter->add_item(desc+" ( "+flt+" )");
+ else
+ filter->add_item("( "+flt+" )");
+ }
+
+ filter->add_item("All Files (*)");
+
+}
+
+void EditorFileDialog::clear_filters() {
+
+ filters.clear();
+ update_filters();
+ invalidate();
+}
+void EditorFileDialog::add_filter(const String& p_filter) {
+
+ filters.push_back(p_filter);
+ update_filters();
+ invalidate();
+
+}
+
+String EditorFileDialog::get_current_dir() const {
+
+ return dir->get_text();
+}
+String EditorFileDialog::get_current_file() const {
+
+ return file->get_text();
+}
+String EditorFileDialog::get_current_path() const {
+
+ return dir->get_text().plus_file(file->get_text());
+}
+void EditorFileDialog::set_current_dir(const String& p_dir) {
+
+ dir_access->change_dir(p_dir);
+ update_dir();
+ invalidate();
+ //_push_history();
+
+
+}
+void EditorFileDialog::set_current_file(const String& p_file) {
+
+ file->set_text(p_file);
+ update_dir();
+ invalidate();
+ int lp = p_file.find_last(".");
+ if (lp!=-1) {
+ file->select(0,lp);
+ file->grab_focus();
+ }
+
+ if (is_visible())
+ _request_single_thumbnail(get_current_dir().plus_file(get_current_file()));
+
+
+}
+void EditorFileDialog::set_current_path(const String& p_path) {
+
+ if (!p_path.size())
+ return;
+ int pos=MAX( p_path.find_last("/"), p_path.find_last("\\") );
+ if (pos==-1) {
+
+ set_current_file(p_path);
+ } else {
+
+ String dir=p_path.substr(0,pos);
+ String file=p_path.substr(pos+1,p_path.length());
+ set_current_dir(dir);
+ set_current_file(file);
+ }
+}
+
+
+void EditorFileDialog::set_mode(Mode p_mode) {
+
+ mode=p_mode;
+ switch(mode) {
+
+ case MODE_OPEN_FILE: get_ok()->set_text("Open"); set_title("Open a File"); makedir->hide(); break;
+ case MODE_OPEN_FILES: get_ok()->set_text("Open"); set_title("Open File(s)"); makedir->hide(); break;
+ case MODE_SAVE_FILE: get_ok()->set_text("Save"); set_title("Save a File"); makedir->show(); break;
+ case MODE_OPEN_DIR: get_ok()->set_text("Open"); set_title("Open a Directory"); makedir->show(); break;
+ }
+
+ if (mode==MODE_OPEN_FILES) {
+ item_list->set_select_mode(ItemList::SELECT_MULTI);
+ } else {
+ item_list->set_select_mode(ItemList::SELECT_SINGLE);
+ }
+}
+
+EditorFileDialog::Mode EditorFileDialog::get_mode() const {
+
+ return mode;
+}
+
+void EditorFileDialog::set_access(Access p_access) {
+
+ ERR_FAIL_INDEX(p_access,3);
+ if (access==p_access)
+ return;
+ memdelete( dir_access );
+ switch(p_access) {
+ case ACCESS_FILESYSTEM: {
+
+ dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ } break;
+ case ACCESS_RESOURCES: {
+
+ dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ } break;
+ case ACCESS_USERDATA: {
+
+ dir_access = DirAccess::create(DirAccess::ACCESS_USERDATA);
+ } break;
+ }
+ access=p_access;
+ _update_drives();
+ invalidate();
+ update_filters();
+ update_dir();
+}
+
+void EditorFileDialog::invalidate() {
+
+ if (is_visible()) {
+ update_file_list();
+ invalidated=false;
+ } else {
+ invalidated=true;
+ }
+
+}
+
+EditorFileDialog::Access EditorFileDialog::get_access() const{
+
+ return access;
+}
+
+void EditorFileDialog::_make_dir_confirm() {
+
+
+ Error err = dir_access->make_dir( makedirname->get_text() );
+ if (err==OK) {
+ dir_access->change_dir(makedirname->get_text());
+ invalidate();
+ update_filters();
+ update_dir();
+ _push_history();
+
+ } else {
+ mkdirerr->popup_centered_minsize(Size2(250,50));
+ }
+}
+
+
+void EditorFileDialog::_make_dir() {
+
+ makedialog->popup_centered_minsize(Size2(250,80));
+ makedirname->grab_focus();
+
+}
+
+void EditorFileDialog::_select_drive(int p_idx) {
+
+ String d = drives->get_item_text(p_idx);
+ dir_access->change_dir(d);
+ file->set_text("");
+ invalidate();
+ update_dir();
+ _push_history();
+
+
+}
+
+void EditorFileDialog::_update_drives() {
+
+
+ int dc = dir_access->get_drive_count();
+ if (dc==0 || access!=ACCESS_FILESYSTEM) {
+ drives->hide();
+ } else {
+ drives->clear();
+ drives->show();
+
+ for(int i=0;i<dir_access->get_drive_count();i++) {
+ String d = dir_access->get_drive(i);
+ drives->add_item(dir_access->get_drive(i));
+ }
+
+ drives->select(dir_access->get_current_drive());
+
+ }
+}
+
+
+void EditorFileDialog::_favorite_selected(int p_idx) {
+
+ Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs();
+ ERR_FAIL_INDEX(p_idx,favorited.size());
+
+ dir_access->change_dir(favorited[p_idx]);
+ file->set_text("");
+ invalidate();
+ update_dir();
+ _push_history();
+}
+
+void EditorFileDialog::_favorite_move_up(){
+
+ int current = favorites->get_current();
+
+ if (current>0 && current<favorites->get_item_count()) {
+ Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs();
+
+ int a_idx=favorited.find(String(favorites->get_item_metadata(current-1)));
+ int b_idx=favorited.find(String(favorites->get_item_metadata(current)));
+
+ if (a_idx==-1 || b_idx==-1)
+ return;
+ SWAP(favorited[a_idx],favorited[b_idx]);
+
+ EditorSettings::get_singleton()->set_favorite_dirs(favorited);
+
+ _update_favorites();
+ update_file_list();
+
+ }
+}
+void EditorFileDialog::_favorite_move_down(){
+
+ int current = favorites->get_current();
+
+ if (current>=0 && current<favorites->get_item_count()-1) {
+ Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs();
+
+ int a_idx=favorited.find(String(favorites->get_item_metadata(current+1)));
+ int b_idx=favorited.find(String(favorites->get_item_metadata(current)));
+
+ if (a_idx==-1 || b_idx==-1)
+ return;
+ SWAP(favorited[a_idx],favorited[b_idx]);
+
+ EditorSettings::get_singleton()->set_favorite_dirs(favorited);
+
+ _update_favorites();
+ update_file_list();
+
+ }
+}
+
+
+void EditorFileDialog::_update_favorites() {
+
+ bool res = access==ACCESS_RESOURCES;
+
+ String current = get_current_dir();
+ Ref<Texture> star = get_icon("Favorites","EditorIcons");
+ favorites->clear();
+
+ favorite->set_pressed(false);
+
+
+ Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs();
+ for(int i=0;i<favorited.size();i++) {
+ bool cres = favorited[i].begins_with("res://");
+ if (cres!=res)
+ continue;
+ String name = favorited[i];
+
+ bool setthis = name==current;
+
+ if (res && name=="res://") {
+ name="/";
+ } else {
+ name=name.get_file()+"/";
+ }
+
+
+ //print_line("file: "+name);
+ favorites->add_item(name,star);
+ favorites->set_item_metadata( favorites->get_item_count()-1,favorited[i]);
+
+ if (setthis) {
+ favorite->set_pressed(true);
+ favorites->set_current(favorites->get_item_count()-1);
+ }
+ }
+
+
+}
+
+void EditorFileDialog::_favorite_toggled(bool p_toggle) {
+ bool res = access==ACCESS_RESOURCES;
+
+ String cd = get_current_dir();
+
+ Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs();
+
+ bool found = false;
+ for(int i=0;i<favorited.size();i++) {
+ bool cres = favorited[i].begins_with("res://");
+ if (cres!=res)
+ continue;
+
+ if (favorited[i]==cd) {
+ found=true;
+ break;
+ }
+ }
+
+ if (found) {
+ favorited.erase(cd);
+ favorite->set_pressed(false);
+ } else {
+ favorited.push_back(cd);
+ favorite->set_pressed(true);
+ }
+
+ EditorSettings::get_singleton()->set_favorite_dirs(favorited);
+
+ _update_favorites();
+
+}
+
+void EditorFileDialog::_recent_selected(int p_idx) {
+
+ Vector<String> recentd = EditorSettings::get_singleton()->get_recent_dirs();
+ ERR_FAIL_INDEX(p_idx,recentd.size());
+
+ dir_access->change_dir(recent->get_item_metadata(p_idx));
+ update_file_list();
+ update_dir();
+ _push_history();
+}
+
+void EditorFileDialog::_go_up() {
+
+ dir_access->change_dir("..");
+ update_file_list();
+ update_dir();
+ _push_history();
+
+}
+
+void EditorFileDialog::_go_back(){
+
+ if (local_history_pos<=0) {
+ return;
+ }
+
+ local_history_pos--;
+ dir_access->change_dir(local_history[local_history_pos]);
+ update_file_list();
+ update_dir();
+
+ dir_prev->set_disabled(local_history_pos==0);
+ dir_next->set_disabled(local_history_pos==local_history.size()-1);
+}
+void EditorFileDialog::_go_forward(){
+
+ if (local_history_pos==local_history.size()-1) {
+ return;
+ }
+
+ local_history_pos++;
+ dir_access->change_dir(local_history[local_history_pos]);
+ update_file_list();
+ update_dir();
+
+ dir_prev->set_disabled(local_history_pos==0);
+ dir_next->set_disabled(local_history_pos==local_history.size()-1);
+
+}
+
+bool EditorFileDialog::default_show_hidden_files=true;
+
+void EditorFileDialog::set_display_mode(DisplayMode p_mode) {
+
+ if (display_mode==p_mode)
+ return;
+ if (p_mode==DISPLAY_THUMBNAILS) {
+ mode_list->set_pressed(false);
+ mode_thumbnails->set_pressed(true);
+ } else {
+ mode_thumbnails->set_pressed(false);
+ mode_list->set_pressed(true);
+ }
+ display_mode=p_mode;
+ invalidate();;
+}
+
+EditorFileDialog::DisplayMode EditorFileDialog::get_display_mode() const{
+
+ return display_mode;
+}
+
+
+
+void EditorFileDialog::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_item_selected"),&EditorFileDialog::_item_selected);
+ ObjectTypeDB::bind_method(_MD("_item_db_selected"),&EditorFileDialog::_item_dc_selected);
+ ObjectTypeDB::bind_method(_MD("_dir_entered"),&EditorFileDialog::_dir_entered);
+ ObjectTypeDB::bind_method(_MD("_file_entered"),&EditorFileDialog::_file_entered);
+ ObjectTypeDB::bind_method(_MD("_action_pressed"),&EditorFileDialog::_action_pressed);
+ ObjectTypeDB::bind_method(_MD("_cancel_pressed"),&EditorFileDialog::_cancel_pressed);
+ ObjectTypeDB::bind_method(_MD("_filter_selected"),&EditorFileDialog::_filter_selected);
+ ObjectTypeDB::bind_method(_MD("_save_confirm_pressed"),&EditorFileDialog::_save_confirm_pressed);
+
+ ObjectTypeDB::bind_method(_MD("clear_filters"),&EditorFileDialog::clear_filters);
+ ObjectTypeDB::bind_method(_MD("add_filter","filter"),&EditorFileDialog::add_filter);
+ ObjectTypeDB::bind_method(_MD("get_current_dir"),&EditorFileDialog::get_current_dir);
+ ObjectTypeDB::bind_method(_MD("get_current_file"),&EditorFileDialog::get_current_file);
+ ObjectTypeDB::bind_method(_MD("get_current_path"),&EditorFileDialog::get_current_path);
+ ObjectTypeDB::bind_method(_MD("set_current_dir","dir"),&EditorFileDialog::set_current_dir);
+ ObjectTypeDB::bind_method(_MD("set_current_file","file"),&EditorFileDialog::set_current_file);
+ ObjectTypeDB::bind_method(_MD("set_current_path","path"),&EditorFileDialog::set_current_path);
+ ObjectTypeDB::bind_method(_MD("set_mode","mode"),&EditorFileDialog::set_mode);
+ ObjectTypeDB::bind_method(_MD("get_mode"),&EditorFileDialog::get_mode);
+ ObjectTypeDB::bind_method(_MD("get_vbox:VBoxContainer"),&EditorFileDialog::get_vbox);
+ ObjectTypeDB::bind_method(_MD("set_access","access"),&EditorFileDialog::set_access);
+ ObjectTypeDB::bind_method(_MD("get_access"),&EditorFileDialog::get_access);
+ ObjectTypeDB::bind_method(_MD("set_show_hidden_files"),&EditorFileDialog::set_show_hidden_files);
+ ObjectTypeDB::bind_method(_MD("is_showing_hidden_files"),&EditorFileDialog::is_showing_hidden_files);
+ ObjectTypeDB::bind_method(_MD("_select_drive"),&EditorFileDialog::_select_drive);
+ ObjectTypeDB::bind_method(_MD("_make_dir"),&EditorFileDialog::_make_dir);
+ ObjectTypeDB::bind_method(_MD("_make_dir_confirm"),&EditorFileDialog::_make_dir_confirm);
+ ObjectTypeDB::bind_method(_MD("_update_file_list"),&EditorFileDialog::update_file_list);
+ ObjectTypeDB::bind_method(_MD("_update_dir"),&EditorFileDialog::update_dir);
+ ObjectTypeDB::bind_method(_MD("_thumbnail_done"),&EditorFileDialog::_thumbnail_done);
+ ObjectTypeDB::bind_method(_MD("set_display_mode","mode"),&EditorFileDialog::set_display_mode);
+ ObjectTypeDB::bind_method(_MD("get_display_mode"),&EditorFileDialog::get_display_mode);
+ ObjectTypeDB::bind_method(_MD("_thumbnail_result"),&EditorFileDialog::_thumbnail_result);
+
+ ObjectTypeDB::bind_method(_MD("_recent_selected"),&EditorFileDialog::_recent_selected);
+ ObjectTypeDB::bind_method(_MD("_go_back"),&EditorFileDialog::_go_back);
+ ObjectTypeDB::bind_method(_MD("_go_forward"),&EditorFileDialog::_go_forward);
+ ObjectTypeDB::bind_method(_MD("_go_up"),&EditorFileDialog::_go_up);
+
+ ObjectTypeDB::bind_method(_MD("_favorite_toggled"),&EditorFileDialog::_favorite_toggled);
+ ObjectTypeDB::bind_method(_MD("_favorite_selected"),&EditorFileDialog::_favorite_selected);
+ ObjectTypeDB::bind_method(_MD("_favorite_move_up"),&EditorFileDialog::_favorite_move_up);
+ ObjectTypeDB::bind_method(_MD("_favorite_move_down"),&EditorFileDialog::_favorite_move_down);
+
+ ObjectTypeDB::bind_method(_MD("invalidate"),&EditorFileDialog::invalidate);
+
+ ADD_SIGNAL(MethodInfo("file_selected",PropertyInfo( Variant::STRING,"path")));
+ ADD_SIGNAL(MethodInfo("files_selected",PropertyInfo( Variant::STRING_ARRAY,"paths")));
+ ADD_SIGNAL(MethodInfo("dir_selected",PropertyInfo( Variant::STRING,"dir")));
+
+ BIND_CONSTANT( MODE_OPEN_FILE );
+ BIND_CONSTANT( MODE_OPEN_FILES );
+ BIND_CONSTANT( MODE_OPEN_DIR );
+ BIND_CONSTANT( MODE_SAVE_FILE );
+
+ BIND_CONSTANT( ACCESS_RESOURCES );
+ BIND_CONSTANT( ACCESS_USERDATA );
+ BIND_CONSTANT( ACCESS_FILESYSTEM );
+
+}
+
+
+void EditorFileDialog::set_show_hidden_files(bool p_show) {
+ show_hidden_files=p_show;
+ invalidate();
+}
+
+bool EditorFileDialog::is_showing_hidden_files() const {
+ return show_hidden_files;
+}
+
+void EditorFileDialog::set_default_show_hidden_files(bool p_show) {
+ default_show_hidden_files=p_show;
+}
+
+void EditorFileDialog::_save_to_recent() {
+
+ String dir = get_current_dir();
+ Vector<String> recent = EditorSettings::get_singleton()->get_recent_dirs();
+
+ const int max=20;
+ int count=0;
+ bool res=dir.begins_with("res://");
+
+ for(int i=0;i<recent.size();i++) {
+ bool cres=recent[i].begins_with("res://");
+ if (recent[i]==dir || (res==cres && count>max)) {
+ recent.remove(i);
+ i--;
+ } else {
+ count++;
+ }
+ }
+
+ recent.insert(0,dir);
+
+ EditorSettings::get_singleton()->set_recent_dirs(recent);
+
+
+}
+
+EditorFileDialog::EditorFileDialog() {
+
+ show_hidden_files=true;
+ display_mode=DISPLAY_THUMBNAILS;
+ local_history_pos=0;
+
+ VBoxContainer *vbc = memnew( VBoxContainer );
+ add_child(vbc);
+ set_child_rect(vbc);
+
+ mode=MODE_SAVE_FILE;
+ set_title("Save a File");
+
+ HBoxContainer *pathhb = memnew( HBoxContainer );
+
+ dir_prev = memnew( ToolButton );
+ dir_next = memnew( ToolButton );
+ dir_up = memnew( ToolButton );
+
+ pathhb->add_child(dir_prev);
+ pathhb->add_child(dir_next);
+ pathhb->add_child(dir_up);
+
+ dir_prev->connect("pressed",this,"_go_back");
+ dir_next->connect("pressed",this,"_go_forward");
+ dir_up->connect("pressed",this,"_go_up");
+
+ dir = memnew(LineEdit);
+ pathhb->add_child(dir);
+ dir->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ favorite = memnew( ToolButton );
+ favorite->set_toggle_mode(true);
+ favorite->connect("toggled",this,"_favorite_toggled");
+ pathhb->add_child(favorite);
+
+ mode_thumbnails = memnew( ToolButton );
+ mode_thumbnails->connect("pressed",this,"set_display_mode",varray(DISPLAY_THUMBNAILS));
+ mode_thumbnails->set_toggle_mode(true);
+ mode_thumbnails->set_pressed(true);
+ pathhb->add_child(mode_thumbnails);
+ mode_list = memnew( ToolButton );
+ mode_list->connect("pressed",this,"set_display_mode",varray(DISPLAY_LIST));
+ mode_list->set_toggle_mode(true);
+ pathhb->add_child(mode_list);
+
+ drives = memnew( OptionButton );
+ pathhb->add_child(drives);
+ drives->connect("item_selected",this,"_select_drive");
+
+ makedir = memnew( Button );
+ makedir->set_text("Create Folder");
+ makedir->connect("pressed",this,"_make_dir");
+ pathhb->add_child(makedir);
+
+ list_hb = memnew( HBoxContainer );
+
+ vbc->add_margin_child("Path:",pathhb);
+ vbc->add_child(list_hb);
+ list_hb->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ VBoxContainer *fav_vb = memnew( VBoxContainer );
+ list_hb->add_child(fav_vb);
+ HBoxContainer *fav_hb = memnew( HBoxContainer );
+ fav_vb->add_child(fav_hb);
+ fav_hb->add_child(memnew(Label("Favorites:")));
+ fav_hb->add_spacer();
+ fav_up = memnew( ToolButton );
+ fav_hb->add_child(fav_up);
+ fav_up->connect("pressed",this,"_favorite_move_up");
+ fav_down = memnew( ToolButton );
+ fav_hb->add_child(fav_down);
+ fav_down->connect("pressed",this,"_favorite_move_down");
+ fav_rm = memnew( ToolButton );
+ fav_hb->add_child(fav_rm);
+ fav_rm->hide(); // redundant
+
+ MarginContainer *fav_mv = memnew( MarginContainer );
+ fav_vb->add_child(fav_mv);
+ fav_mv->set_v_size_flags(SIZE_EXPAND_FILL);
+ favorites = memnew( ItemList );
+ fav_mv->add_child(favorites);
+ favorites->connect("item_selected",this,"_favorite_selected");
+
+ recent = memnew( ItemList );
+ fav_vb->add_margin_child("Recent:",recent,true);
+ recent->connect("item_selected",this,"_recent_selected");
+
+ VBoxContainer *item_vb = memnew( VBoxContainer );
+ list_hb->add_child(item_vb);
+ item_vb->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ item_list = memnew( ItemList );
+ item_list->set_v_size_flags(SIZE_EXPAND_FILL);
+ item_vb->add_margin_child("Directories & Files:",item_list,true);
+
+ HBoxContainer* filter_hb = memnew( HBoxContainer );
+ item_vb->add_child(filter_hb);
+
+ VBoxContainer *filter_vb = memnew( VBoxContainer );
+ filter_hb->add_child(filter_vb);
+ filter_vb->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ preview_vb = memnew( VBoxContainer );
+ filter_hb->add_child(preview_vb);
+ CenterContainer *prev_cc = memnew( CenterContainer );
+ preview_vb->add_margin_child("Preview:",prev_cc);
+ preview = memnew( TextureFrame );
+ prev_cc->add_child(preview);
+ preview_vb->hide();
+
+
+ file = memnew(LineEdit);
+ //add_child(file);
+ filter_vb->add_margin_child("File:",file);
+
+
+ filter = memnew( OptionButton );
+ //add_child(filter);
+ filter_vb->add_margin_child("Filter:",filter);
+ filter->set_clip_text(true);//too many extensions overflow it
+
+ dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ access=ACCESS_RESOURCES;
+ _update_drives();
+
+
+ connect("confirmed", this,"_action_pressed");
+ //cancel->connect("pressed", this,"_cancel_pressed");
+ item_list->connect("item_selected", this,"_item_selected",varray(),CONNECT_DEFERRED);
+ item_list->connect("item_activated", this,"_item_db_selected",varray());
+ dir->connect("text_entered", this,"_dir_entered");
+ file->connect("text_entered", this,"_file_entered");
+ filter->connect("item_selected", this,"_filter_selected");
+
+
+ confirm_save = memnew( ConfirmationDialog );
+ confirm_save->set_as_toplevel(true);
+ add_child(confirm_save);
+
+
+ confirm_save->connect("confirmed", this,"_save_confirm_pressed");
+
+ makedialog = memnew( ConfirmationDialog );
+ makedialog->set_title("Create Folder");
+ VBoxContainer *makevb= memnew( VBoxContainer );
+ makedialog->add_child(makevb);
+ makedialog->set_child_rect(makevb);
+ makedirname = memnew( LineEdit );
+ makevb->add_margin_child("Name:",makedirname);
+ add_child(makedialog);
+ makedialog->register_text_enter(makedirname);
+ makedialog->connect("confirmed",this,"_make_dir_confirm");
+ mkdirerr = memnew( AcceptDialog );
+ mkdirerr->set_text("Could not create folder.");
+ add_child(mkdirerr);
+
+ exterr = memnew( AcceptDialog );
+ exterr->set_text("Must use a valid extension.");
+ add_child(exterr);
+
+
+ //update_file_list();
+ update_filters();
+ update_dir();
+
+ set_hide_on_ok(false);
+ vbox=vbc;
+
+
+ invalidated=true;
+ if (register_func)
+ register_func(this);
+
+ preview_wheel_timeout=0;
+ preview_wheel_index=0;
+ preview_waiting=false;
+
+}
+
+
+EditorFileDialog::~EditorFileDialog() {
+
+ if (unregister_func)
+ unregister_func(this);
+ memdelete(dir_access);
+}
+
+
+void EditorLineEditFileChooser::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_browse"),&EditorLineEditFileChooser::_browse);
+ ObjectTypeDB::bind_method(_MD("_chosen"),&EditorLineEditFileChooser::_chosen);
+ ObjectTypeDB::bind_method(_MD("get_button:Button"),&EditorLineEditFileChooser::get_button);
+ ObjectTypeDB::bind_method(_MD("get_line_edit:LineEdit"),&EditorLineEditFileChooser::get_line_edit);
+ ObjectTypeDB::bind_method(_MD("get_file_dialog:EditorFileDialog"),&EditorLineEditFileChooser::get_file_dialog);
+
+}
+
+void EditorLineEditFileChooser::_chosen(const String& p_text){
+
+ line_edit->set_text(p_text);
+ line_edit->emit_signal("text_entered",p_text);
+}
+
+void EditorLineEditFileChooser::_browse() {
+
+ dialog->popup_centered_ratio();
+}
+
+EditorLineEditFileChooser::EditorLineEditFileChooser() {
+
+ line_edit = memnew( LineEdit );
+ add_child(line_edit);
+ line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ button = memnew( Button );
+ button->set_text(" .. ");
+ add_child(button);
+ button->connect("pressed",this,"_browse");
+ dialog = memnew( EditorFileDialog);
+ add_child(dialog);
+ dialog->connect("file_selected",this,"_chosen");
+ dialog->connect("dir_selected",this,"_chosen");
+ dialog->connect("files_selected",this,"_chosen");
+
+}
diff --git a/tools/editor/editor_file_dialog.h b/tools/editor/editor_file_dialog.h
new file mode 100644
index 0000000000..6cfd970516
--- /dev/null
+++ b/tools/editor/editor_file_dialog.h
@@ -0,0 +1,245 @@
+/*************************************************************************/
+/* file_dialog.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+
+#ifndef EDITORFILEDIALOG_H
+#define EDITORFILEDIALOG_H
+
+#include "scene/gui/dialogs.h"
+#include "scene/gui/item_list.h"
+#include "scene/gui/line_edit.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/dialogs.h"
+#include "os/dir_access.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/texture_frame.h"
+#include "scene/gui/tool_button.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class EditorFileDialog : public ConfirmationDialog {
+
+ OBJ_TYPE( EditorFileDialog, ConfirmationDialog );
+
+public:
+
+ enum DisplayMode {
+ DISPLAY_THUMBNAILS,
+ DISPLAY_LIST
+ };
+
+
+ enum Access {
+ ACCESS_RESOURCES,
+ ACCESS_USERDATA,
+ ACCESS_FILESYSTEM
+ };
+
+
+ enum Mode {
+ MODE_OPEN_FILE,
+ MODE_OPEN_FILES,
+ MODE_OPEN_DIR,
+ MODE_SAVE_FILE,
+ };
+
+ typedef Ref<Texture> (*GetIconFunc)(const String&);
+ typedef void (*RegisterFunc)(EditorFileDialog*);
+
+ static GetIconFunc get_icon_func;
+ static GetIconFunc get_large_icon_func;
+ static RegisterFunc register_func;
+ static RegisterFunc unregister_func;
+
+private:
+
+ ConfirmationDialog *makedialog;
+ LineEdit *makedirname;
+
+ Button *makedir;
+ Access access;
+ //Button *action;
+ VBoxContainer *vbox;
+ Mode mode;
+ LineEdit *dir;
+
+ ToolButton *dir_prev;
+ ToolButton *dir_next;
+ ToolButton *dir_up;
+
+ OptionButton *drives;
+ ItemList *item_list;
+ TextureFrame *preview;
+ VBoxContainer *preview_vb;
+ HBoxContainer *list_hb;
+ LineEdit *file;
+ AcceptDialog *mkdirerr;
+ AcceptDialog *exterr;
+ OptionButton *filter;
+ DirAccess *dir_access;
+ ConfirmationDialog *confirm_save;
+ ToolButton *mode_thumbnails;
+ ToolButton *mode_list;
+
+
+ ToolButton *favorite;
+
+ ToolButton *fav_up;
+ ToolButton *fav_down;
+ ToolButton *fav_rm;
+
+ ItemList *favorites;
+ ItemList *recent;
+
+ Vector<String> local_history;
+ int local_history_pos;
+ void _push_history();
+
+ Vector<String> filters;
+
+ bool preview_waiting;
+ int preview_wheel_index;
+ float preview_wheel_timeout;
+ static bool default_show_hidden_files;
+ bool show_hidden_files;
+ DisplayMode display_mode;
+
+ bool invalidated;
+
+ void update_dir();
+ void update_file_list();
+ void update_filters();
+
+ void _update_favorites();
+ void _favorite_toggled(bool p_toggle);
+ void _favorite_selected(int p_idx);
+ void _favorite_move_up();
+ void _favorite_move_down();
+
+
+
+ void _recent_selected(int p_idx);
+
+ void _item_selected(int p_item);
+ void _item_dc_selected(int p_item);
+
+ void _select_drive(int p_idx);
+ void _dir_entered(String p_dir);
+ void _file_entered(const String& p_file);
+ void _action_pressed();
+ void _save_confirm_pressed();
+ void _cancel_pressed();
+ void _filter_selected(int);
+ void _make_dir();
+ void _make_dir_confirm();
+
+ void _update_drives();
+
+ void _go_up();
+ void _go_back();
+ void _go_forward();
+
+ virtual void _post_popup();
+
+ void _save_to_recent();
+ //callback funtion is callback(String p_path,Ref<Texture> preview,Variant udata) preview null if could not load
+
+ void _thumbnail_result(const String& p_path,const Ref<Texture>& p_preview, const Variant& p_udata);
+ void _thumbnail_done(const String& p_path,const Ref<Texture>& p_preview, const Variant& p_udata);
+ void _request_single_thumbnail(const String& p_path);
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+ //bind helpers
+public:
+
+ void clear_filters();
+ void add_filter(const String& p_filter);
+
+ void set_enable_multiple_selection(bool p_enable);
+ Vector<String> get_selected_files() const;
+
+ String get_current_dir() const;
+ String get_current_file() const;
+ String get_current_path() const;
+ void set_current_dir(const String& p_dir);
+ void set_current_file(const String& p_file);
+ void set_current_path(const String& p_path);
+
+ void set_display_mode(DisplayMode p_mode);
+ DisplayMode get_display_mode() const;
+
+ void set_mode(Mode p_mode);
+ Mode get_mode() const;
+
+ VBoxContainer *get_vbox();
+ LineEdit *get_line_edit() { return file; }
+
+ void set_access(Access p_access);
+ Access get_access() const;
+
+ void set_show_hidden_files(bool p_show);
+ bool is_showing_hidden_files() const;
+
+ static void set_default_show_hidden_files(bool p_show);
+
+ void invalidate();
+
+ EditorFileDialog();
+ ~EditorFileDialog();
+
+};
+
+class EditorLineEditFileChooser : public HBoxContainer {
+
+ OBJ_TYPE( EditorLineEditFileChooser, HBoxContainer );
+ Button *button;
+ LineEdit *line_edit;
+ EditorFileDialog *dialog;
+
+ void _chosen(const String& p_text);
+ void _browse();
+protected:
+ static void _bind_methods();
+public:
+
+ Button *get_button() { return button; }
+ LineEdit *get_line_edit() { return line_edit; }
+ EditorFileDialog *get_file_dialog() { return dialog; }
+
+ EditorLineEditFileChooser();
+};
+
+VARIANT_ENUM_CAST( EditorFileDialog::Mode );
+VARIANT_ENUM_CAST( EditorFileDialog::Access );
+VARIANT_ENUM_CAST( EditorFileDialog::DisplayMode );
+
+#endif // EDITORFILEDIALOG_H
diff --git a/tools/editor/editor_file_system.cpp b/tools/editor/editor_file_system.cpp
index 5d72928e9c..33e4a15c85 100644
--- a/tools/editor/editor_file_system.cpp
+++ b/tools/editor/editor_file_system.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,7 +33,7 @@
#include "os/file_access.h"
#include "editor_node.h"
#include "io/resource_saver.h"
-
+#include "editor_settings.h"
EditorFileSystem *EditorFileSystem::singleton=NULL;
@@ -94,6 +94,12 @@ bool EditorFileSystemDirectory::get_file_meta(int p_idx) const {
return files[p_idx].meta.enabled;
}
+Vector<String> EditorFileSystemDirectory::get_file_deps(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,files.size(),Vector<String>());
+ return files[p_idx].meta.deps;
+
+}
Vector<String> EditorFileSystemDirectory::get_missing_sources(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx,files.size(),Vector<String>());
@@ -118,7 +124,7 @@ bool EditorFileSystemDirectory::is_missing_sources(int p_idx) const {
return false;
}
-String EditorFileSystemDirectory::get_file_type(int p_idx) const {
+StringName EditorFileSystemDirectory::get_file_type(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx,files.size(),"");
return files[p_idx].type;
@@ -198,6 +204,13 @@ EditorFileSystemDirectory::ImportMeta EditorFileSystem::_get_meta(const String&
}
m.import_editor=imd->get_editor();
}
+
+ List<String> deps;
+ ResourceLoader::get_dependencies(p_path,&deps);
+ for(List<String>::Element *E=deps.front();E;E=E->next()) {
+ m.deps.push_back(E->get());
+ }
+
return m;
}
@@ -357,7 +370,9 @@ void EditorFileSystem::_scan_scenes() {
String project=Globals::get_singleton()->get_resource_path();
- FileAccess *f =FileAccess::open(project+"/.fscache",FileAccess::READ);
+
+ String fscache = EditorSettings::get_singleton()->get_project_settings_path().plus_file("filesystem_cache");
+ FileAccess *f =FileAccess::open(fscache,FileAccess::READ);
if (f) {
//read the disk cache
@@ -395,7 +410,7 @@ void EditorFileSystem::_scan_scenes() {
} else {
Vector<String> split = l.split("::");
- ERR_CONTINUE( split.size() != 4);
+ ERR_CONTINUE( split.size() != 5);
String name = split[0];
String file;
@@ -427,6 +442,15 @@ void EditorFileSystem::_scan_scenes() {
}
}
+ String deps = split[4].strip_edges();
+ if (deps.length()) {
+ Vector<String> dp = deps.split("<>");
+ for(int i=0;i<dp.size();i++) {
+ String path=dp[i];
+ fc.meta.deps.push_back(path);
+ }
+ }
+
file_cache[name]=fc;
ERR_CONTINUE(!dc);
@@ -470,7 +494,9 @@ void EditorFileSystem::_scan_scenes() {
//save back the findings
- f=FileAccess::open(project+"/.fscache",FileAccess::WRITE);
+// String fscache = EditorSettings::get_singleton()->get_project_settings_path().plus_file("file_cache");
+
+ f=FileAccess::open(fscache,FileAccess::WRITE);
_save_type_cache_fs(scandir,f);
f->close();
memdelete(f);
@@ -526,6 +552,7 @@ void EditorFileSystem::scan() {
thread = Thread::create(_thread_func,this,s);
//tree->hide();
//progress->show();
+
}
@@ -794,6 +821,14 @@ void EditorFileSystem::_save_type_cache_fs(DirItem *p_dir,FileAccess *p_file) {
}
}
+ s+="::";
+ for(int j=0;j<p_dir->files[i]->meta.deps.size();j++) {
+
+ if (j>0)
+ s+="<>";
+ s+=p_dir->files[i]->meta.deps[j];
+ }
+
p_file->store_line(s);
}
@@ -940,19 +975,19 @@ String EditorFileSystem::get_file_type(const String& p_file) const {
EditorFileSystemDirectory *EditorFileSystem::get_path(const String& p_path) {
if (!filesystem || scanning)
- return false;
+ return NULL;
String f = Globals::get_singleton()->localize_path(p_path);
if (!f.begins_with("res://"))
- return false;
+ return NULL;
f=f.substr(6,f.length());
f=f.replace("\\","/");
if (f==String())
- return filesystem;
+ return filesystem;
if (f.ends_with("/"))
f=f.substr(0,f.length()-1);
@@ -960,7 +995,7 @@ EditorFileSystemDirectory *EditorFileSystem::get_path(const String& p_path) {
Vector<String> path = f.split("/");
if (path.size()==0)
- return false;
+ return NULL;
EditorFileSystemDirectory *fs=filesystem;
@@ -989,6 +1024,7 @@ EditorFileSystemDirectory *EditorFileSystem::get_path(const String& p_path) {
void EditorFileSystem::_resource_saved(const String& p_path){
+ print_line("resource saved: "+p_path);
EditorFileSystem::get_singleton()->update_file(p_path);
}
@@ -1032,6 +1068,13 @@ void EditorFileSystem::update_file(const String& p_file) {
return;
}
+ if (!FileAccess::exists(p_file)) {
+ //was removed
+ fs->files.remove(cpos);
+ call_deferred("emit_signal","filesystem_changed"); //update later
+ return;
+
+ }
String type = ResourceLoader::get_resource_type(p_file);
diff --git a/tools/editor/editor_file_system.h b/tools/editor/editor_file_system.h
index 2d14f9012f..f79dd209ef 100644
--- a/tools/editor/editor_file_system.h
+++ b/tools/editor/editor_file_system.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -59,13 +59,14 @@ class EditorFileSystemDirectory : public Object {
Vector<Source> sources;
String import_editor;
+ Vector<String> deps;
bool enabled;
};
struct FileInfo {
String file;
- String type;
+ StringName type;
uint64_t modified_time;
ImportMeta meta;
@@ -87,10 +88,11 @@ public:
int get_file_count() const;
String get_file(int p_idx) const;
String get_file_path(int p_idx) const;
- String get_file_type(int p_idx) const;
+ StringName get_file_type(int p_idx) const;
bool get_file_meta(int p_idx) const;
bool is_missing_sources(int p_idx) const;
Vector<String> get_missing_sources(int p_idx) const;
+ Vector<String> get_file_deps(int p_idx) const;
EditorFileSystemDirectory *get_parent();
@@ -120,7 +122,7 @@ class EditorFileSystem : public Node {
String path;
String name;
Vector<DirItem*> dirs;
- Vector<SceneItem*> files;
+ Vector<SceneItem*> files;
~DirItem();
};
@@ -149,6 +151,7 @@ class EditorFileSystem : public Node {
String type;
uint64_t modification_time;
EditorFileSystemDirectory::ImportMeta meta;
+ Vector<String> deps;
};
struct DirCache {
diff --git a/tools/editor/editor_fonts.cpp b/tools/editor/editor_fonts.cpp
index 86f1e9283c..f145f1ddef 100644
--- a/tools/editor/editor_fonts.cpp
+++ b/tools/editor/editor_fonts.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -26,50 +26,50 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "editor_fonts.h"
-#include "doc_font.h"
-#include "doc_title_font.h"
-#include "doc_code_font.h"
-
-static Ref<Font> make_font(int p_height,int p_ascent, int p_valign, int p_charcount, const int *p_chars,const Ref<Texture> &p_texture) {
-
-
- Ref<Font> font( memnew( Font ) );
- font->add_texture( p_texture );
-
- for (int i=0;i<p_charcount;i++) {
-
- const int *c = &p_chars[i*8];
-
- int chr=c[0];
- Rect2 frect;
- frect.pos.x=c[1];
- frect.pos.y=c[2];
- frect.size.x=c[3];
- frect.size.y=c[4];
- Point2 align( c[5], c[6]+p_valign);
- int advance=c[7];
-
-
- font->add_char( chr, 0, frect, align,advance );
-
- }
-
- font->set_height( p_height );
- font->set_ascent( p_ascent );
-
- return font;
-}
-
-
-void editor_register_fonts(Ref<Theme> p_theme) {
-
-
- Ref<Font> doc_font = make_font(_bi_font_doc_font_height,_bi_font_doc_font_ascent,0,_bi_font_doc_font_charcount,_bi_font_doc_font_characters,p_theme->get_icon("DocFont","EditorIcons"));
- Ref<Font> doc_code_font = make_font(_bi_font_doc_code_font_height,_bi_font_doc_code_font_ascent,0,_bi_font_doc_code_font_charcount,_bi_font_doc_code_font_characters,p_theme->get_icon("DocCodeFont","EditorIcons"));
- Ref<Font> doc_title_font = make_font(_bi_font_doc_title_font_height,_bi_font_doc_title_font_ascent,0,_bi_font_doc_title_font_charcount,_bi_font_doc_title_font_characters,p_theme->get_icon("DocTitleFont","EditorIcons"));
- p_theme->set_font("doc","EditorFonts",doc_font);
- p_theme->set_font("doc_code","EditorFonts",doc_code_font);
- p_theme->set_font("doc_title","EditorFonts",doc_title_font);
-
-}
+#include "editor_fonts.h"
+#include "doc_font.h"
+#include "doc_title_font.h"
+#include "doc_code_font.h"
+
+static Ref<Font> make_font(int p_height,int p_ascent, int p_valign, int p_charcount, const int *p_chars,const Ref<Texture> &p_texture) {
+
+
+ Ref<Font> font( memnew( Font ) );
+ font->add_texture( p_texture );
+
+ for (int i=0;i<p_charcount;i++) {
+
+ const int *c = &p_chars[i*8];
+
+ int chr=c[0];
+ Rect2 frect;
+ frect.pos.x=c[1];
+ frect.pos.y=c[2];
+ frect.size.x=c[3];
+ frect.size.y=c[4];
+ Point2 align( c[5], c[6]+p_valign);
+ int advance=c[7];
+
+
+ font->add_char( chr, 0, frect, align,advance );
+
+ }
+
+ font->set_height( p_height );
+ font->set_ascent( p_ascent );
+
+ return font;
+}
+
+
+void editor_register_fonts(Ref<Theme> p_theme) {
+
+
+ Ref<Font> doc_font = make_font(_bi_font_doc_font_height,_bi_font_doc_font_ascent,0,_bi_font_doc_font_charcount,_bi_font_doc_font_characters,p_theme->get_icon("DocFont","EditorIcons"));
+ Ref<Font> doc_code_font = make_font(_bi_font_doc_code_font_height,_bi_font_doc_code_font_ascent,0,_bi_font_doc_code_font_charcount,_bi_font_doc_code_font_characters,p_theme->get_icon("DocCodeFont","EditorIcons"));
+ Ref<Font> doc_title_font = make_font(_bi_font_doc_title_font_height,_bi_font_doc_title_font_ascent,0,_bi_font_doc_title_font_charcount,_bi_font_doc_title_font_characters,p_theme->get_icon("DocTitleFont","EditorIcons"));
+ p_theme->set_font("doc","EditorFonts",doc_font);
+ p_theme->set_font("doc_code","EditorFonts",doc_code_font);
+ p_theme->set_font("doc_title","EditorFonts",doc_title_font);
+
+}
diff --git a/tools/editor/editor_fonts.h b/tools/editor/editor_fonts.h
index a3973513b3..cc990a560c 100644
--- a/tools/editor/editor_fonts.h
+++ b/tools/editor/editor_fonts.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -26,11 +26,11 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef EDITOR_FONTS_H
-#define EDITOR_FONTS_H
-
-#include "scene/resources/theme.h"
-
-void editor_register_fonts(Ref<Theme> p_theme);
-
-#endif
+#ifndef EDITOR_FONTS_H
+#define EDITOR_FONTS_H
+
+#include "scene/resources/theme.h"
+
+void editor_register_fonts(Ref<Theme> p_theme);
+
+#endif
diff --git a/tools/editor/editor_help.cpp b/tools/editor/editor_help.cpp
index 8408436a6c..321ac76240 100644
--- a/tools/editor/editor_help.cpp
+++ b/tools/editor/editor_help.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -79,7 +79,7 @@ void EditorHelpSearch::_update_search() {
_parse_fs(EditorFileSystem::get_singleton()->get_filesystem());
*/
- List<String> type_list;
+ List<StringName> type_list;
ObjectTypeDB::get_type_list(&type_list);
DocData *doc=EditorHelp::get_doc_data();
@@ -300,9 +300,9 @@ void EditorHelpSearch::_bind_methods() {
}
-EditorHelpSearch::EditorHelpSearch(EditorNode *p_editor) {
+EditorHelpSearch::EditorHelpSearch() {
- editor=p_editor;
+ editor=EditorNode::get_singleton();
VBoxContainer *vbc = memnew( VBoxContainer );
add_child(vbc);
set_child_rect(vbc);
@@ -318,17 +318,138 @@ EditorHelpSearch::EditorHelpSearch(EditorNode *p_editor) {
search_box->connect("input_event",this,"_sbox_input");
search_options = memnew( Tree );
vbc->add_margin_child("Matches:",search_options,true);
- get_ok()->set_text("View");
+ get_ok()->set_text("Open");
get_ok()->set_disabled(true);
register_text_enter(search_box);
set_hide_on_ok(false);
search_options->connect("item_activated",this,"_confirmed");
set_title("Search Classes");
+
// search_options->set_hide_root(true);
}
+/////////////////////////////////
+
+////////////////////////////////////
+/// /////////////////////////////////
+
+
+
+void EditorHelpIndex::add_type(const String& p_type,HashMap<String,TreeItem*>& p_types,TreeItem *p_root) {
+
+ if (p_types.has(p_type))
+ return;
+// if (!ObjectTypeDB::is_type(p_type,base) || p_type==base)
+// return;
+
+ String inherits=EditorHelp::get_doc_data()->class_list[p_type].inherits;
+
+ TreeItem *parent=p_root;
+
+
+ if (inherits.length()) {
+
+ if (!p_types.has(inherits)) {
+
+ add_type(inherits,p_types,p_root);
+ }
+
+ if (p_types.has(inherits) )
+ parent=p_types[inherits];
+ }
+
+ TreeItem *item = class_list->create_item(parent);
+ item->set_metadata(0,p_type);
+ item->set_tooltip(0,EditorHelp::get_doc_data()->class_list[p_type].brief_description);
+ item->set_text(0,p_type);
+
+
+ if (has_icon(p_type,"EditorIcons")) {
+
+ item->set_icon(0, get_icon(p_type,"EditorIcons"));
+ }
+
+ p_types[p_type]=item;
+}
+
+
+void EditorHelpIndex::_tree_item_selected() {
+
+
+ TreeItem *s=class_list->get_selected();
+ if (!s)
+ return;
+
+ emit_signal("open_class",s->get_text(0));
+
+ hide();
+
+ //_goto_desc(s->get_text(0));
+
+}
+
+void EditorHelpIndex::select_class(const String& p_class) {
+
+ if (!tree_item_map.has(p_class))
+ return;
+ tree_item_map[p_class]->select(0);
+ class_list->ensure_cursor_is_visible();
+}
+
+void EditorHelpIndex::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+
+ class_list->clear();
+ tree_item_map.clear();
+ TreeItem *root = class_list->create_item();
+ class_list->set_hide_root(true);
+ connect("confirmed",this,"_tree_item_selected");
+
+
+ for(Map<String,DocData::ClassDoc>::Element *E=EditorHelp::get_doc_data()->class_list.front();E;E=E->next()) {
+
+
+ add_type(E->key(),tree_item_map,root);
+ }
+
+ }
+}
+
+void EditorHelpIndex::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_tree_item_selected",&EditorHelpIndex::_tree_item_selected);
+ ObjectTypeDB::bind_method("select_class",&EditorHelpIndex::select_class);
+ ADD_SIGNAL( MethodInfo("open_class"));
+}
+
+
+
+EditorHelpIndex::EditorHelpIndex() {
+
+
+ VBoxContainer *vbc = memnew( VBoxContainer );
+ add_child(vbc);
+ set_child_rect(vbc);
+
+ class_list = memnew( Tree );
+ vbc->add_margin_child("Class List: ",class_list,true);
+ class_list->set_v_size_flags(SIZE_EXPAND_FILL);
+
+
+ class_list->connect("item_activated",this,"_tree_item_selected");
+
+
+ get_ok()->set_text("Open");
+}
+
+
+/////////////////////////////////
+
+////////////////////////////////////
+/// /////////////////////////////////
DocData *EditorHelp::doc=NULL;
void EditorHelp::_unhandled_key_input(const InputEvent& p_ev) {
@@ -339,8 +460,6 @@ void EditorHelp::_unhandled_key_input(const InputEvent& p_ev) {
search->grab_focus();
search->select_all();
- } else if (p_ev.key.mod.shift && p_ev.key.scancode==KEY_F1) {
- class_search->popup();
}
}
@@ -351,17 +470,19 @@ void EditorHelp::_search(const String&) {
String stext=search->get_text();
- bool keep = prev_search==stext && class_list->get_selected() && prev_search_page==class_list->get_selected()->get_text(0);
+ bool keep = prev_search==stext;
- class_desc->search(stext);
+ bool ret = class_desc->search(stext, keep);
+ if (!ret) {
+ class_desc->search(stext, false);
+ }
prev_search=stext;
- if (class_list->get_selected())
- prev_search_page=class_list->get_selected()->get_text(0);
}
+#if 0
void EditorHelp::_button_pressed(int p_idx) {
if (p_idx==PAGE_CLASS_LIST) {
@@ -399,16 +520,11 @@ void EditorHelp::_button_pressed(int p_idx) {
} else if (p_idx==PAGE_SEARCH) {
_search("");
- } else if (p_idx==CLASS_SEARCH) {
-
- class_search->popup();
}
-
-
}
-
+#endif
void EditorHelp::_class_list_select(const String& p_select) {
@@ -417,16 +533,28 @@ void EditorHelp::_class_list_select(const String& p_select) {
void EditorHelp::_class_desc_select(const String& p_select) {
+
+
+// print_line("LINK: "+p_select);
if (p_select.begins_with("#")) {
- _goto_desc(p_select.substr(1,p_select.length()));
+ //_goto_desc(p_select.substr(1,p_select.length()));
+ emit_signal("go_to_help","class_name:"+p_select.substr(1,p_select.length()));
return;
} else if (p_select.begins_with("@")) {
String m = p_select.substr(1,p_select.length());
- if (!method_line.has(m))
- return;
- class_desc->scroll_to_line(method_line[m]);
- return;
+
+ if (m.find(".")!=-1) {
+ //must go somewhere else
+
+ emit_signal("go_to_help","class_method:"+m.get_slice(".",0)+":"+m.get_slice(".",0));
+ } else {
+
+ if (!method_line.has(m))
+ return;
+ class_desc->scroll_to_line(method_line[m]);
+ }
+
}
@@ -449,68 +577,40 @@ void EditorHelp::_add_type(const String& p_type) {
}
-void EditorHelp::_update_history_buttons() {
-
- back->set_disabled(history_pos<2);
- forward->set_disabled(history_pos>=history.size());
-
-}
-
-
void EditorHelp::_scroll_changed(double p_scroll) {
if (scroll_locked)
return;
- int p = history_pos -1;
- if (p<0 || p>=history.size())
- return;
-
if (class_desc->get_v_scroll()->is_hidden())
p_scroll=0;
- history[p].scroll=p_scroll;
+ //history[p].scroll=p_scroll;
}
-Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_vscr) {
+Error EditorHelp::_goto_desc(const String& p_class,int p_vscr) {
//ERR_FAIL_COND(!doc->class_list.has(p_class));
if (!doc->class_list.has(p_class))
return ERR_DOES_NOT_EXIST;
- if (tree_item_map.has(p_class)) {
+ //if (tree_item_map.has(p_class)) {
select_locked = true;
- tree_item_map[p_class]->select(0);
- class_list->ensure_cursor_is_visible();
- }
+ //}
class_desc->show();
//tabs->set_current_tab(PAGE_CLASS_DESC);
- edited_class->set_pressed(true);
- class_list_button->set_pressed(false);
description_line=0;
- if (p_class==edited_class->get_text())
+ if (p_class==edited_class)
return OK; //already there
scroll_locked=true;
- if (p_update_history) {
-
- history.resize(history_pos);
- history_pos++;
- History h;
- h.c=p_class;
- h.scroll=0;
- history.push_back(h);
- _update_history_buttons();
- class_desc->get_v_scroll()->set_val(0);
- }
-
class_desc->clear();
method_line.clear();
- edited_class->set_text(p_class);
+ edited_class=p_class;
//edited_class->show();
@@ -529,7 +629,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color"));
class_desc->add_text("Class: ");
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/base_type_color"));
- class_desc->add_text(p_class);
+ _add_text(p_class);
class_desc->pop();
class_desc->pop();
class_desc->pop();
@@ -560,7 +660,13 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
//class_desc->add_newline();
class_desc->add_newline();
+ class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color"));
+ class_desc->push_font( get_font("normal","Fonts") );
+ class_desc->push_indent(1);
_add_text(cd.brief_description);
+ class_desc->pop();
+ class_desc->pop();
+ class_desc->pop();
class_desc->add_newline();
class_desc->add_newline();
}
@@ -591,7 +697,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
class_desc->push_meta("@"+cd.methods[i].name);
}
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color"));
- class_desc->add_text(cd.methods[i].name);
+ _add_text(cd.methods[i].name);
class_desc->pop();
if (cd.methods[i].description!="")
class_desc->pop();
@@ -603,13 +709,14 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
if (j>0)
class_desc->add_text(", ");
_add_type(cd.methods[i].arguments[j].type);
- class_desc->add_text(" "+cd.methods[i].arguments[j].name);
+ class_desc->add_text(" ");
+ _add_text(cd.methods[i].arguments[j].name);
if (cd.methods[i].arguments[j].default_value!="") {
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color"));
class_desc->add_text("=");
class_desc->pop();
- class_desc->add_text(cd.methods[i].arguments[j].default_value);
+ _add_text(cd.methods[i].arguments[j].default_value);
}
class_desc->pop();
@@ -621,7 +728,8 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
if (cd.methods[i].qualifiers!="") {
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color"));
- class_desc->add_text(" "+cd.methods[i].qualifiers);
+ class_desc->add_text(" ");
+ _add_text(cd.methods[i].qualifiers);
class_desc->pop();
}
@@ -655,7 +763,8 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
class_desc->push_font(doc_code_font);
_add_type(cd.properties[i].type);
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color"));
- class_desc->add_text(" "+cd.properties[i].name);
+ class_desc->add_text(" ");
+ _add_text(cd.properties[i].name);
class_desc->pop();
class_desc->pop();
@@ -663,7 +772,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
class_desc->push_font(doc_font);
class_desc->add_text(" ");
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/comment_color"));
- class_desc->add_text(cd.properties[i].description);
+ _add_text(cd.properties[i].description);
class_desc->pop();
class_desc->pop();
@@ -698,7 +807,8 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
class_desc->push_font(doc_code_font);
_add_type(cd.theme_properties[i].type);
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color"));
- class_desc->add_text(" "+cd.theme_properties[i].name);
+ class_desc->add_text(" ");
+ _add_text(cd.theme_properties[i].name);
class_desc->pop();
class_desc->pop();
@@ -706,7 +816,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
class_desc->push_font(doc_font);
class_desc->add_text(" ");
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/comment_color"));
- class_desc->add_text(cd.theme_properties[i].description);
+ _add_text(cd.theme_properties[i].description);
class_desc->pop();
class_desc->pop();
@@ -736,11 +846,11 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
for(int i=0;i<cd.signals.size();i++) {
signal_line[cd.signals[i].name]=class_desc->get_line_count()-2; //gets overriden if description
- class_desc->push_font(doc_code_font);
+ class_desc->push_font(doc_code_font); // monofont
//_add_type("void");
//class_desc->add_text(" ");
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color"));
- class_desc->add_text(cd.signals[i].name);
+ _add_text(cd.signals[i].name);
class_desc->pop();
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color"));
class_desc->add_text(cd.signals[i].arguments.size()?"( ":"(");
@@ -750,13 +860,14 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
if (j>0)
class_desc->add_text(", ");
_add_type(cd.signals[i].arguments[j].type);
- class_desc->add_text(" "+cd.signals[i].arguments[j].name);
+ class_desc->add_text(" ");
+ _add_text(cd.signals[i].arguments[j].name);
if (cd.signals[i].arguments[j].default_value!="") {
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color"));
class_desc->add_text("=");
class_desc->pop();
- class_desc->add_text(cd.signals[i].arguments[j].default_value);
+ _add_text(cd.signals[i].arguments[j].default_value);
}
class_desc->pop();
@@ -765,14 +876,15 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color"));
class_desc->add_text(cd.signals[i].arguments.size()?" )":")");
class_desc->pop();
+ class_desc->pop(); // end monofont
if (cd.signals[i].description!="") {
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/comment_color"));
- class_desc->add_text(" "+cd.signals[i].description);
+ class_desc->add_text(" ");
+ _add_text(cd.signals[i].description);
class_desc->pop();
}
- class_desc->pop();//monofont
class_desc->add_newline();
}
@@ -800,20 +912,20 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
constant_line[cd.constants[i].name]=class_desc->get_line_count()-2;
class_desc->push_font(doc_code_font);
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/base_type_color"));
- class_desc->add_text(cd.constants[i].name);
+ _add_text(cd.constants[i].name);
class_desc->pop();
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color"));
class_desc->add_text(" = ");
class_desc->pop();
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color"));
- class_desc->add_text(cd.constants[i].value);
+ _add_text(cd.constants[i].value);
class_desc->pop();
class_desc->pop();
if (cd.constants[i].description!="") {
class_desc->push_font(doc_font);
class_desc->add_text(" ");
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/comment_color"));
- class_desc->add_text(cd.constants[i].description);
+ _add_text(cd.constants[i].description);
class_desc->pop();
class_desc->pop();
}
@@ -838,7 +950,13 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
class_desc->add_newline();
class_desc->add_newline();
+ class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color"));
+ class_desc->push_font( get_font("normal","Fonts") );
+ class_desc->push_indent(1);
_add_text(cd.description);
+ class_desc->pop();
+ class_desc->pop();
+ class_desc->pop();
class_desc->add_newline();
class_desc->add_newline();
}
@@ -864,7 +982,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
class_desc->add_text(" ");
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color"));
- class_desc->add_text(cd.methods[i].name);
+ _add_text(cd.methods[i].name);
class_desc->pop();
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color"));
class_desc->add_text(cd.methods[i].arguments.size()?"( ":"(");
@@ -874,13 +992,14 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
if (j>0)
class_desc->add_text(", ");
_add_type(cd.methods[i].arguments[j].type);
- class_desc->add_text(" "+cd.methods[i].arguments[j].name);
+ class_desc->add_text(" ");
+ _add_text(cd.methods[i].arguments[j].name);
if (cd.methods[i].arguments[j].default_value!="") {
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color"));
class_desc->add_text("=");
class_desc->pop();
- class_desc->add_text(cd.methods[i].arguments[j].default_value);
+ _add_text(cd.methods[i].arguments[j].default_value);
}
class_desc->pop();
@@ -892,7 +1011,8 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
if (cd.methods[i].qualifiers!="") {
class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color"));
- class_desc->add_text(" "+cd.methods[i].qualifiers);
+ class_desc->add_text(" ");
+ _add_text(cd.methods[i].qualifiers);
class_desc->pop();
}
@@ -900,8 +1020,14 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
class_desc->pop();
class_desc->add_newline();
- class_desc->add_newline();
+ class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color"));
+ class_desc->push_font( get_font("normal","Fonts") );
+ class_desc->push_indent(1);
_add_text(cd.methods[i].description);
+ class_desc->pop();
+ class_desc->pop();
+ class_desc->pop();
+ class_desc->add_newline();
class_desc->add_newline();
class_desc->add_newline();
@@ -913,10 +1039,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v
}
- if (!p_update_history) {
- class_desc->get_v_scroll()->set_val(history[history_pos-1].scroll);
- }
scroll_locked=false;
@@ -927,8 +1050,6 @@ void EditorHelp::_request_help(const String& p_string) {
Error err = _goto_desc(p_string);
if (err==OK) {
editor->call("_editor_select",3);
- } else {
- class_search->popup(p_string);
}
//100 palabras
}
@@ -975,9 +1096,9 @@ void EditorHelp::_help_callback(const String& p_topic) {
void EditorHelp::_add_text(const String& p_bbcode) {
- class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color"));
+ /*class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color"));
class_desc->push_font( get_font("normal","Fonts") );
- class_desc->push_indent(1);
+ class_desc->push_indent(1);*/
int pos = 0;
List<String> tag_stack;
@@ -1055,7 +1176,7 @@ void EditorHelp::_add_text(const String& p_bbcode) {
} else if (tag=="i") {
//use italics font
- //class_desc->push_font(get_font("italic","Fonts"));
+ class_desc->push_font(get_font("italic","Fonts"));
pos=brk_end+1;
tag_stack.push_front(tag);
} else if (tag=="code") {
@@ -1191,69 +1312,18 @@ void EditorHelp::_add_text(const String& p_bbcode) {
}
}
+ /*class_desc->pop();
class_desc->pop();
- class_desc->pop();
+ class_desc->pop();*/
}
-void EditorHelp::add_type(const String& p_type,HashMap<String,TreeItem*>& p_types,TreeItem *p_root) {
-
- if (p_types.has(p_type))
- return;
-// if (!ObjectTypeDB::is_type(p_type,base) || p_type==base)
-// return;
-
- String inherits=doc->class_list[p_type].inherits;
-
- TreeItem *parent=p_root;
-
-
- if (inherits.length()) {
-
- if (!p_types.has(inherits)) {
-
- add_type(inherits,p_types,p_root);
- }
-
- if (p_types.has(inherits) )
- parent=p_types[inherits];
- }
-
- TreeItem *item = class_list->create_item(parent);
- item->set_metadata(0,p_type);
- item->set_tooltip(0,doc->class_list[p_type].brief_description);
- item->set_text(0,p_type);
-
-
- if (has_icon(p_type,"EditorIcons")) {
-
- item->set_icon(0, get_icon(p_type,"EditorIcons"));
- }
-
- p_types[p_type]=item;
-}
-
void EditorHelp::_update_doc() {
- class_list->clear();
-
- List<String> type_list;
-
- tree_item_map.clear();
-
- TreeItem *root = class_list->create_item();
- class_list->set_hide_root(true);
- List<String>::Element *I=type_list.front();
-
- for(Map<String,DocData::ClassDoc>::Element *E=doc->class_list.front();E;E=E->next()) {
-
-
- add_type(E->key(),tree_item_map,root);
- }
}
@@ -1277,8 +1347,8 @@ void EditorHelp::_notification(int p_what) {
case NOTIFICATION_READY: {
- forward->set_icon(get_icon("Forward","EditorIcons"));
- back->set_icon(get_icon("Back","EditorIcons"));
+// forward->set_icon(get_icon("Forward","EditorIcons"));
+// back->set_icon(get_icon("Back","EditorIcons"));
_update_doc();
editor->connect("request_help",this,"_request_help");
@@ -1286,222 +1356,118 @@ void EditorHelp::_notification(int p_what) {
}
}
-void EditorHelp::_tree_item_selected() {
+void EditorHelp::go_to_help(const String& p_help) {
- if (select_locked) {
- select_locked = false;
- return;
- }
- TreeItem *s=class_list->get_selected();
- if (!s)
- return;
- select_locked=true;
- _goto_desc(s->get_text(0));
- select_locked=false;
+ _help_callback(p_help);
}
-void EditorHelp::_bind_methods() {
-
- ObjectTypeDB::bind_method("_class_list_select",&EditorHelp::_class_list_select);
- ObjectTypeDB::bind_method("_class_desc_select",&EditorHelp::_class_desc_select);
- ObjectTypeDB::bind_method("_button_pressed",&EditorHelp::_button_pressed);
- ObjectTypeDB::bind_method("_scroll_changed",&EditorHelp::_scroll_changed);
- ObjectTypeDB::bind_method("_request_help",&EditorHelp::_request_help);
- ObjectTypeDB::bind_method("_unhandled_key_input",&EditorHelp::_unhandled_key_input);
- ObjectTypeDB::bind_method("_search",&EditorHelp::_search);
- ObjectTypeDB::bind_method("_tree_item_selected",&EditorHelp::_tree_item_selected);
- ObjectTypeDB::bind_method("_help_callback",&EditorHelp::_help_callback);
+void EditorHelp::go_to_class(const String& p_class,int p_scroll) {
+ _goto_desc(p_class,p_scroll);
}
-EditorHelp::EditorHelp(EditorNode *p_editor) {
-
- editor=p_editor;
-
- VBoxContainer *vbc = this;
-
- HBoxContainer *panel_hb = memnew( HBoxContainer );
-
- Button *b = memnew( Button );
- b->set_text("Class List");
- panel_hb->add_child(b);
- vbc->add_child(panel_hb);
- b->set_toggle_mode(true);
- b->set_pressed(true);
- b->connect("pressed",this,"_button_pressed",make_binds(PAGE_CLASS_LIST));
- class_list_button=b;
- class_list_button->hide();
-
- b = memnew( Button );
- b->set_text("Class");
- panel_hb->add_child(b);
- edited_class=b;
- edited_class->hide();
- b->set_toggle_mode(true);
- b->connect("pressed",this,"_button_pressed",make_binds(PAGE_CLASS_DESC));
-
- b = memnew( Button );
- b->set_text("Search in Classes");
- panel_hb->add_child(b);
- b->connect("pressed",this,"_button_pressed",make_binds(CLASS_SEARCH));
-
- Control *expand = memnew( Control );
- expand->set_h_size_flags(SIZE_EXPAND_FILL);
- panel_hb->add_child(expand);
-
- b = memnew( Button );
- panel_hb->add_child(b);
- back=b;
- b->connect("pressed",this,"_button_pressed",make_binds(PAGE_CLASS_PREV));
-
- b = memnew( Button );
- panel_hb->add_child(b);
- forward=b;
- b->connect("pressed",this,"_button_pressed",make_binds(PAGE_CLASS_NEXT));
-
- Separator *hs = memnew( VSeparator );
- panel_hb->add_child(hs);
- EmptyControl *ec = memnew( EmptyControl );
- ec->set_minsize(Size2(200,1));
- panel_hb->add_child(ec);
- search = memnew( LineEdit );
- ec->add_child(search);
- search->set_area_as_parent_rect();
- search->connect("text_entered",this,"_search");
-
- b = memnew( Button );
- b->set_text("Find");
- panel_hb->add_child(b);
- b->connect("pressed",this,"_button_pressed",make_binds(PAGE_SEARCH));
-
- hs = memnew( VSeparator );
- panel_hb->add_child(hs);
-
- h_split = memnew( HSplitContainer );
- h_split->set_v_size_flags(SIZE_EXPAND_FILL);
+void EditorHelp::popup_search() {
- vbc->add_child(h_split);
-
- class_list = memnew( Tree );
- h_split->add_child(class_list);
- //class_list->connect("meta_clicked",this,"_class_list_select");
- //class_list->set_selection_enabled(true);
-
- {
- PanelContainer *pc = memnew( PanelContainer );
- pc->add_style_override("panel",get_stylebox("normal","TextEdit"));
- h_split->add_child(pc);
- class_desc = memnew( RichTextLabel );
- pc->add_child(class_desc);
- class_desc->connect("meta_clicked",this,"_class_desc_select");
- }
-
- class_desc->get_v_scroll()->connect("value_changed",this,"_scroll_changed");
- class_desc->set_selection_enabled(true);
- editor=p_editor;
- history_pos=0;
- scroll_locked=false;
- select_locked=false;
- set_process_unhandled_key_input(true);
- h_split->set_split_offset(200);
- class_list->connect("cell_selected",this,"_tree_item_selected");
- class_desc->hide();
-
- class_search = memnew( EditorHelpSearch(editor) );
- editor->get_gui_base()->add_child(class_search);
- class_search->connect("go_to_help",this,"_help_callback");
-// prev_search_page=-1;
-}
-
-EditorHelp::~EditorHelp() {
- if (doc)
- memdelete(doc);
+ search_dialog->popup_centered(Size2(250,80));
+ search->grab_focus();
}
+void EditorHelp::_search_cbk() {
-void EditorHelpPlugin::edit(Object *p_object) {
-
- if (!p_object->cast_to<Script>())
- return;
-
- //editor_help->edit(p_object->cast_to<Script>());
+ _search(search->get_text());
}
-bool EditorHelpPlugin::handles(Object *p_object) const {
+String EditorHelp::get_class_name() {
- return false;
+ return edited_class;
}
-void EditorHelpPlugin::make_visible(bool p_visible) {
-
- if (p_visible) {
- editor_help->show();
- } else {
-
- editor_help->hide();
- }
-
+void EditorHelp::search_again() {
+ _search(prev_search);
}
-void EditorHelpPlugin::selected_notify() {
+int EditorHelp::get_scroll() const {
- //editor_help->ensure_select_current();
+ return class_desc->get_v_scroll()->get_val();
}
+void EditorHelp::set_scroll(int p_scroll) {
-Dictionary EditorHelpPlugin::get_state() const {
-
- return Dictionary();
-}
-void EditorHelpPlugin::set_state(const Dictionary& p_state) {
+ class_desc->get_v_scroll()->set_val(p_scroll);
- //editor_help->set_state(p_state);
}
-void EditorHelpPlugin::clear() {
- //editor_help->clear();
-}
+void EditorHelp::_bind_methods() {
-void EditorHelpPlugin::save_external_data() {
+ ObjectTypeDB::bind_method("_class_list_select",&EditorHelp::_class_list_select);
+ ObjectTypeDB::bind_method("_class_desc_select",&EditorHelp::_class_desc_select);
+// ObjectTypeDB::bind_method("_button_pressed",&EditorHelp::_button_pressed);
+ ObjectTypeDB::bind_method("_scroll_changed",&EditorHelp::_scroll_changed);
+ ObjectTypeDB::bind_method("_request_help",&EditorHelp::_request_help);
+ ObjectTypeDB::bind_method("_unhandled_key_input",&EditorHelp::_unhandled_key_input);
+ ObjectTypeDB::bind_method("_search",&EditorHelp::_search);
+ ObjectTypeDB::bind_method("_search_cbk",&EditorHelp::_search_cbk);
- //editor_help->save_external_data();
-}
+ ObjectTypeDB::bind_method("_help_callback",&EditorHelp::_help_callback);
-void EditorHelpPlugin::apply_changes() {
+ ADD_SIGNAL(MethodInfo("go_to_help"));
- //editor_help->apply_helps();
}
-void EditorHelpPlugin::restore_global_state() {
+EditorHelp::EditorHelp() {
- //if (bool(EDITOR_DEF("text_editor/restore_helps_on_load",true))) {
-// editor_help->_load_files_state();
- //}
+ editor=EditorNode::get_singleton();
-}
+ VBoxContainer *vbc = this;
-void EditorHelpPlugin::save_global_state() {
- //if (bool(EDITOR_DEF("text_editor/restore_helps_on_load",true))) {
-// editor_help->_save_files_state();
-// }
+ //class_list->connect("meta_clicked",this,"_class_list_select");
+ //class_list->set_selection_enabled(true);
-}
+ {
+ Panel *pc = memnew( Panel );
+ Ref<StyleBoxFlat> style( memnew( StyleBoxFlat ) );
+ style->set_bg_color( EditorSettings::get_singleton()->get("text_editor/background_color") );
+ pc->set_v_size_flags(SIZE_EXPAND_FILL);
+ pc->add_style_override("panel", style); //get_stylebox("normal","TextEdit"));
+ vbc->add_child(pc);
+ class_desc = memnew( RichTextLabel );
+ pc->add_child(class_desc);
+ class_desc->set_area_as_parent_rect(8);
+ class_desc->connect("meta_clicked",this,"_class_desc_select");
+ }
+ class_desc->get_v_scroll()->connect("value_changed",this,"_scroll_changed");
+ class_desc->set_selection_enabled(true);
+
+ scroll_locked=false;
+ select_locked=false;
+ set_process_unhandled_key_input(true);
+ class_desc->hide();
-EditorHelpPlugin::EditorHelpPlugin(EditorNode *p_node) {
+ search_dialog = memnew( ConfirmationDialog );
+ add_child(search_dialog);
+ VBoxContainer *search_vb = memnew( VBoxContainer );
+ search_dialog->add_child(search_vb);
+ search_dialog->set_child_rect(search_vb);
+ search = memnew( LineEdit );
+ search_dialog->register_text_enter(search);
+ search_vb->add_margin_child("Search Text",search);
+ search_dialog->get_ok()->set_text("Find");
+ search_dialog->connect("confirmed",this,"_search_cbk");
+ search_dialog->set_hide_on_ok(false);
+ search_dialog->set_self_opacity(0.8);
- editor=p_node;
- editor_help = memnew( EditorHelp(p_node) );
- editor->get_viewport()->add_child(editor_help);
- editor_help->set_area_as_parent_rect();
- editor_help->hide();
+ /*class_search = memnew( EditorHelpSearch(editor) );
+ editor->get_gui_base()->add_child(class_search);
+ class_search->connect("go_to_help",this,"_help_callback");*/
+// prev_search_page=-1;
}
+EditorHelp::~EditorHelp() {
-EditorHelpPlugin::~EditorHelpPlugin()
-{
}
+
diff --git a/tools/editor/editor_help.h b/tools/editor/editor_help.h
index 1c2b704b98..b5ee6eca6c 100644
--- a/tools/editor/editor_help.h
+++ b/tools/editor/editor_help.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -70,9 +70,29 @@ public:
void popup(const String& p_term="");
- EditorHelpSearch(EditorNode *p_editor);
+ EditorHelpSearch();
};
+class EditorHelpIndex : public ConfirmationDialog {
+ OBJ_TYPE( EditorHelpIndex, ConfirmationDialog );
+
+
+ Tree *class_list;
+ HashMap<String,TreeItem*> tree_item_map;
+
+ void _tree_item_selected();
+ void add_type(const String& p_type,HashMap<String,TreeItem*>& p_types,TreeItem *p_root);
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ void select_class(const String& p_class);
+
+ EditorHelpIndex();
+};
class EditorHelp : public VBoxContainer {
@@ -91,17 +111,11 @@ class EditorHelp : public VBoxContainer {
};
- struct History {
- String c;
- int scroll;
- };
-
- Vector<History> history;
- int history_pos;
bool select_locked;
String prev_search;
- String prev_search_page;
+
+ String edited_class;
EditorNode *editor;
Map<String,int> method_line;
@@ -111,21 +125,17 @@ class EditorHelp : public VBoxContainer {
Map<String,int> constant_line;
int description_line;
- Tree *class_list;
RichTextLabel *class_desc;
HSplitContainer *h_split;
static DocData *doc;
- Button *class_list_button;
- Button *edited_class;
- Button *back;
- Button *forward;
+
+ ConfirmationDialog *search_dialog;
LineEdit *search;
- String base_path;
- HashMap<String,TreeItem*> tree_item_map;
+ String base_path;
void _help_callback(const String& p_topic);
@@ -133,25 +143,24 @@ class EditorHelp : public VBoxContainer {
void _add_text(const String& p_text);
bool scroll_locked;
- void _button_pressed(int p_idx);
+ //void _button_pressed(int p_idx);
void _add_type(const String& p_type);
void _scroll_changed(double p_scroll);
void _class_list_select(const String& p_select);
void _class_desc_select(const String& p_select);
- Error _goto_desc(const String& p_class,bool p_update_history=true,int p_vscr=-1);
- void _update_history_buttons();
+ Error _goto_desc(const String& p_class, int p_vscr=-1);
+ //void _update_history_buttons();
void _update_doc();
void _request_help(const String& p_string);
void _search(const String& p_str);
+ void _search_cbk();
void _unhandled_key_input(const InputEvent& p_ev);
- void add_type(const String& p_type,HashMap<String,TreeItem*>& p_types,TreeItem *p_root);
- void _tree_item_selected();
- EditorHelpSearch *class_search;
+
protected:
@@ -163,41 +172,25 @@ public:
static void generate_doc();
static DocData *get_doc_data() { return doc; }
- EditorHelp(EditorNode *p_editor=NULL);
- ~EditorHelp();
-};
-
-
+ void go_to_help(const String& p_help);
+ void go_to_class(const String& p_class,int p_scroll=0);
-class EditorHelpPlugin : public EditorPlugin {
+ void popup_search();
+ void search_again();
- OBJ_TYPE( EditorHelpPlugin, EditorPlugin );
+ String get_class_name();
- EditorHelp *editor_help;
- EditorNode *editor;
-public:
+ void set_focused() { class_desc->grab_focus(); }
- virtual String get_name() const { return "Help"; }
- bool has_main_screen() const { return true; }
- virtual void edit(Object *p_node);
- virtual bool handles(Object *p_node) const;
- virtual void make_visible(bool p_visible);
- virtual void selected_notify();
+ int get_scroll() const;
+ void set_scroll(int p_scroll);
- Dictionary get_state() const;
- virtual void set_state(const Dictionary& p_state);
- virtual void clear();
-
- virtual void save_external_data();
- virtual void apply_changes();
+ EditorHelp();
+ ~EditorHelp();
+};
- virtual void restore_global_state();
- virtual void save_global_state();
- EditorHelpPlugin(EditorNode *p_node);
- ~EditorHelpPlugin();
-};
#endif // EDITOR_HELP_H
diff --git a/tools/editor/editor_icons.h b/tools/editor/editor_icons.h
index 4cd08bba03..910febc895 100644
--- a/tools/editor/editor_icons.h
+++ b/tools/editor/editor_icons.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/editor_import_export.cpp b/tools/editor/editor_import_export.cpp
index a511e78863..f52c6e67a2 100644
--- a/tools/editor/editor_import_export.cpp
+++ b/tools/editor/editor_import_export.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -40,6 +40,7 @@
#include "io/resource_saver.h"
#include "io/md5.h"
#include "io_plugins/editor_texture_import_plugin.h"
+#include "tools/editor/plugins/script_editor_plugin.h"
String EditorImportPlugin::validate_source_path(const String& p_path) {
@@ -47,6 +48,7 @@ String EditorImportPlugin::validate_source_path(const String& p_path) {
String rp = Globals::get_singleton()->get_resource_path();
if (!rp.ends_with("/"))
rp+="/";
+
return rp.path_to_file(gp);
}
@@ -253,7 +255,16 @@ static void _add_filter_to_list(Set<StringName>& r_list,const String& p_filter)
}
+Vector<uint8_t> EditorExportPlatform::get_exported_file_default(String& p_fname) const {
+ FileAccess *f = FileAccess::open(p_fname,FileAccess::READ);
+ ERR_FAIL_COND_V(!f,Vector<uint8_t>());
+ Vector<uint8_t> ret;
+ ret.resize(f->get_len());
+ int rbs = f->get_buffer(ret.ptr(),ret.size());
+ memdelete(f);
+ return ret;
+}
Vector<uint8_t> EditorExportPlatform::get_exported_file(String& p_fname) const {
@@ -268,13 +279,9 @@ Vector<uint8_t> EditorExportPlatform::get_exported_file(String& p_fname) const {
}
- FileAccess *f = FileAccess::open(p_fname,FileAccess::READ);
- ERR_FAIL_COND_V(!f,Vector<uint8_t>());
- Vector<uint8_t> ret;
- ret.resize(f->get_len());
- int rbs = f->get_buffer(ret.ptr(),ret.size());
- memdelete(f);
- return ret;
+ return get_exported_file_default(p_fname);
+
+
}
Vector<StringName> EditorExportPlatform::get_dependencies(bool p_bundles) const {
@@ -292,7 +299,11 @@ Vector<StringName> EditorExportPlatform::get_dependencies(bool p_bundles) const
_add_filter_to_list(exported,"*");
} else {
_add_to_list(EditorFileSystem::get_singleton()->get_filesystem(),exported);
- _add_filter_to_list(exported,EditorImportExport::get_singleton()->get_export_custom_filter());
+ String cf = EditorImportExport::get_singleton()->get_export_custom_filter();
+ if (cf!="")
+ cf+=",";
+ cf+="*.flags";
+ _add_filter_to_list(exported,cf);
}
@@ -361,8 +372,12 @@ Vector<StringName> EditorExportPlatform::get_dependencies(bool p_bundles) const
}
}
}
+ String cf = EditorImportExport::get_singleton()->get_export_custom_filter();
+ if (cf!="")
+ cf+=",";
+ cf+="*.flags";
+ _add_filter_to_list(exported,cf);
- _add_filter_to_list(exported,EditorImportExport::get_singleton()->get_export_custom_filter());
}
@@ -547,6 +562,7 @@ Error EditorExportPlatform::export_project_files(EditorExportSaveFunction p_func
group_shrink*=EditorImportExport::get_singleton()->get_export_image_shrink();
switch(EditorImportExport::get_singleton()->image_export_group_get_image_action(E->get())) {
+ case EditorImportExport::IMAGE_ACTION_KEEP:
case EditorImportExport::IMAGE_ACTION_NONE: {
switch(EditorImportExport::get_singleton()->get_export_image_action()) {
@@ -571,7 +587,7 @@ Error EditorExportPlatform::export_project_files(EditorExportSaveFunction p_func
} break; //use default
case EditorImportExport::IMAGE_ACTION_COMPRESS_RAM: {
group_format=EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_RAM;
- } break; //use default
+ } break; //use default
}
String image_list_md5;
@@ -815,13 +831,43 @@ Error EditorExportPlatform::export_project_files(EditorExportSaveFunction p_func
StringName engine_cfg="res://engine.cfg";
+ StringName boot_splash;
+ {
+ String splash=Globals::get_singleton()->get("application/boot_splash"); //avoid splash from being converted
+ splash=splash.strip_edges();
+ if (splash!=String()) {
+ if (!splash.begins_with("res://"))
+ splash="res://"+splash;
+ splash=splash.simplify_path();
+ boot_splash=splash;
+ }
+ }
+ StringName custom_cursor;
+ {
+ String splash=Globals::get_singleton()->get("display/custom_mouse_cursor"); //avoid splash from being converted
+ splash=splash.strip_edges();
+ if (splash!=String()) {
+ if (!splash.begins_with("res://"))
+ splash="res://"+splash;
+ splash=splash.simplify_path();
+ custom_cursor=splash;
+ }
+ }
+
+
+
for(int i=0;i<files.size();i++) {
if (remap_files.has(files[i]) || files[i]==engine_cfg) //gonna be remapped (happened before!)
continue; //from atlas?
String src=files[i];
- Vector<uint8_t> buf = get_exported_file(src);
+ Vector<uint8_t> buf;
+
+ if (src==boot_splash || src==custom_cursor)
+ buf = get_exported_file_default(src); //bootsplash must be kept if used
+ else
+ buf = get_exported_file(src);
ERR_CONTINUE( saved.has(src) );
@@ -896,6 +942,69 @@ Error EditorExportPlatform::export_project_files(EditorExportSaveFunction p_func
return OK;
}
+static int _get_pad(int p_alignment, int p_n) {
+
+ int rest = p_n % p_alignment;
+ int pad = 0;
+ if (rest > 0) {
+ pad = p_alignment - rest;
+ };
+
+ return pad;
+};
+
+void EditorExportPlatform::gen_export_flags(Vector<String> &r_flags, int p_flags) {
+
+ String host = EditorSettings::get_singleton()->get("network/debug_host");
+
+ if (p_flags&EXPORT_DUMB_CLIENT) {
+ int port = EditorSettings::get_singleton()->get("file_server/port");
+ String passwd = EditorSettings::get_singleton()->get("file_server/password");
+ r_flags.push_back("-rfs");
+ r_flags.push_back(host+":"+itos(port));
+ if (passwd!="") {
+ r_flags.push_back("-rfs_pass");
+ r_flags.push_back(passwd);
+ }
+ }
+
+ if (p_flags&EXPORT_REMOTE_DEBUG) {
+
+ r_flags.push_back("-rdebug");
+ r_flags.push_back(host+":"+String::num(GLOBAL_DEF("debug/debug_port", 6007)));
+
+ List<String> breakpoints;
+ ScriptEditor::get_singleton()->get_breakpoints(&breakpoints);
+
+
+ if (breakpoints.size()) {
+
+ r_flags.push_back("-bp");
+ String bpoints;
+ for(const List<String>::Element *E=breakpoints.front();E;E=E->next()) {
+
+ bpoints+=E->get().replace(" ","%20");
+ if (E->next())
+ bpoints+=",";
+ }
+
+ r_flags.push_back(bpoints);
+ }
+
+ }
+
+ if (p_flags&EXPORT_VIEW_COLLISONS) {
+
+ r_flags.push_back("-debugcol");
+ }
+
+ if (p_flags&EXPORT_VIEW_NAVIGATION) {
+
+ r_flags.push_back("-debugnav");
+ }
+
+
+}
Error EditorExportPlatform::save_pack_file(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total) {
@@ -922,11 +1031,19 @@ Error EditorExportPlatform::save_pack_file(void *p_userdata,const String& p_path
pd->ep->step("Storing File: "+p_path,2+p_file*100/p_total);
pd->count++;
pd->ftmp->store_buffer(p_data.ptr(),p_data.size());
+ if (pd->alignment > 1) {
+
+ int pad = _get_pad(pd->alignment, pd->ftmp->get_pos());
+ for (int i=0; i<pad; i++) {
+
+ pd->ftmp->store_8(0);
+ };
+ };
return OK;
}
-Error EditorExportPlatform::save_pack(FileAccess *dst,bool p_make_bundles) {
+Error EditorExportPlatform::save_pack(FileAccess *dst,bool p_make_bundles, int p_alignment) {
EditorProgress ep("savepack","Packing",102);
@@ -944,7 +1061,6 @@ Error EditorExportPlatform::save_pack(FileAccess *dst,bool p_make_bundles) {
dst->store_32(0);
}
-
size_t fcountpos = dst->get_pos();
dst->store_32(0);
@@ -953,11 +1069,20 @@ Error EditorExportPlatform::save_pack(FileAccess *dst,bool p_make_bundles) {
pd.f=dst;
pd.ftmp=tmp;
pd.count=0;
+ pd.alignment = p_alignment;
Error err = export_project_files(save_pack_file,&pd,p_make_bundles);
memdelete(tmp);
if (err)
return err;
+ if (p_alignment > 1) {
+ int pad = _get_pad(p_alignment, dst->get_pos());
+ for (int i=0; i<pad; i++) {
+
+ dst->store_8(0);
+ };
+ };
+
size_t ofsplus = dst->get_pos();
//append file
@@ -994,7 +1119,7 @@ Error EditorExportPlatform::save_pack(FileAccess *dst,bool p_make_bundles) {
return OK;
}
-Error EditorExportPlatformPC::export_project(const String& p_path, bool p_debug, bool p_dumb) {
+Error EditorExportPlatformPC::export_project(const String& p_path, bool p_debug, int p_flags) {
@@ -1084,30 +1209,42 @@ bool EditorExportPlatformPC::can_export(String *r_error) const {
String exe_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/";
- if (!FileAccess::exists(exe_path+debug_binary32) || !FileAccess::exists(exe_path+release_binary32)) {
+ if (use64 && (!FileAccess::exists(exe_path+debug_binary64) || !FileAccess::exists(exe_path+release_binary64))) {
valid=false;
- err="No 32 bits export templates found.\nDownload and install export templates.\n";
+ err="No 64 bits export templates found.\nDownload and install export templates.\n";
}
- if (!FileAccess::exists(exe_path+debug_binary64) || !FileAccess::exists(exe_path+release_binary64)) {
+
+ if (!use64 && (!FileAccess::exists(exe_path+debug_binary32) || !FileAccess::exists(exe_path+release_binary32))) {
valid=false;
- err="No 64 bits export templates found.\nDownload and install export templates.\n";
+ err="No 32 bits export templates found.\nDownload and install export templates.\n";
+ }
+
+ if(custom_debug_binary=="" && custom_release_binary=="") {
+ if (r_error) *r_error=err;
+ return valid;
}
+ bool dvalid = true;
+ bool rvalid = true;
- if (custom_debug_binary!="" && !FileAccess::exists(custom_debug_binary)) {
- valid=false;
- err+="Custom debug binary not found.\n";
+ if(!FileAccess::exists(custom_debug_binary)) {
+ dvalid = false;
+ err = "Custom debug binary not found.\n";
}
- if (custom_release_binary!="" && !FileAccess::exists(custom_release_binary)) {
- valid=false;
- err+="Custom release binary not found.\n";
+ if(!FileAccess::exists(custom_release_binary)) {
+ rvalid = false;
+ err = "Custom release binary not found.\n";
}
+ if (dvalid || rvalid)
+ valid = true;
+ else
+ valid = false;
+
if (r_error)
*r_error=err;
return valid;
-
}
@@ -1133,10 +1270,36 @@ EditorImportExport* EditorImportExport::singleton=NULL;
void EditorImportExport::add_import_plugin(const Ref<EditorImportPlugin>& p_plugin) {
+ // Need to make sure the name is unique if we are going to lookup by it
+ ERR_FAIL_COND(by_idx.has(p_plugin->get_name()));
+
by_idx[ p_plugin->get_name() ]=plugins.size();
plugins.push_back(p_plugin);
}
+void EditorImportExport::remove_import_plugin(const Ref<EditorImportPlugin>& p_plugin) {
+
+ String plugin_name = p_plugin->get_name();
+
+ // Keep the indices the same
+ // Find the index of the target plugin
+ ERR_FAIL_COND(!by_idx.has(plugin_name));
+ int idx = by_idx[plugin_name];
+ int last_idx = plugins.size() - 1;
+
+ // Swap the last plugin and the target one
+ SWAP(plugins[idx], plugins[last_idx]);
+
+ // Update the index of the old last one
+ by_idx[plugins[idx]->get_name()] = idx;
+
+ // Remove the target plugin's by_idx entry
+ by_idx.erase(plugin_name);
+
+ // Erase the plugin
+ plugins.remove(last_idx);
+}
+
int EditorImportExport::get_import_plugin_count() const{
return plugins.size();
@@ -1269,12 +1432,12 @@ EditorImportExport::ImageAction EditorImportExport::get_export_image_action() co
return image_action;
}
-void EditorImportExport::set_export_image_shrink(int p_shrink) {
+void EditorImportExport::set_export_image_shrink(float p_shrink) {
image_shrink=p_shrink;
}
-int EditorImportExport::get_export_image_shrink() const{
+float EditorImportExport::get_export_image_shrink() const{
return image_shrink;
}
@@ -1345,12 +1508,12 @@ bool EditorImportExport::image_export_group_get_make_atlas(const StringName& p_e
return image_groups[p_export_group].make_atlas;
}
-void EditorImportExport::image_export_group_set_shrink(const StringName& p_export_group,int p_amount){
+void EditorImportExport::image_export_group_set_shrink(const StringName& p_export_group,float p_amount){
ERR_FAIL_COND(!image_groups.has(p_export_group));
image_groups[p_export_group].shrink=p_amount;
}
-int EditorImportExport::image_export_group_get_shrink(const StringName& p_export_group) const{
+float EditorImportExport::image_export_group_get_shrink(const StringName& p_export_group) const{
ERR_FAIL_COND_V(!image_groups.has(p_export_group),1);
return image_groups[p_export_group].shrink;
@@ -1504,6 +1667,8 @@ void EditorImportExport::load_config() {
g.action=IMAGE_ACTION_COMPRESS_RAM;
else if (action=="compress_disk")
g.action=IMAGE_ACTION_COMPRESS_DISK;
+ else if (action=="keep")
+ g.action=IMAGE_ACTION_KEEP;
}
if (d.has("atlas"))
@@ -1553,6 +1718,17 @@ void EditorImportExport::load_config() {
}
}
+ if (cf->has_section("convert_samples")) {
+
+ if (cf->has_section_key("convert_samples","max_hz"))
+ sample_action_max_hz=cf->get_value("convert_samples","max_hz");
+
+ if (cf->has_section_key("convert_samples","trim"))
+ sample_action_trim=cf->get_value("convert_samples","trim");
+ }
+
+
+
}
@@ -1631,6 +1807,7 @@ void EditorImportExport::save_config() {
case IMAGE_ACTION_NONE: d["action"]="default"; break;
case IMAGE_ACTION_COMPRESS_RAM: d["action"]="compress_ram"; break;
case IMAGE_ACTION_COMPRESS_DISK: d["action"]="compress_disk"; break;
+ case IMAGE_ACTION_KEEP: d["action"]="keep"; break;
}
@@ -1662,6 +1839,14 @@ void EditorImportExport::save_config() {
cf->set_value("script","encrypt_key",script_key);
+ switch(sample_action) {
+ case SAMPLE_ACTION_NONE: cf->set_value("convert_samples","action","none"); break;
+ case SAMPLE_ACTION_COMPRESS_RAM: cf->set_value("convert_samples","action","compress_ram"); break;
+ }
+
+ cf->set_value("convert_samples","max_hz",sample_action_max_hz);
+ cf->set_value("convert_samples","trim",sample_action_trim);
+
cf->save("res://export.cfg");
}
@@ -1687,6 +1872,35 @@ String EditorImportExport::script_get_encryption_key() const{
}
+void EditorImportExport::sample_set_action(SampleAction p_action) {
+
+ sample_action=p_action;
+}
+
+EditorImportExport::SampleAction EditorImportExport::sample_get_action() const{
+
+ return sample_action;
+}
+
+void EditorImportExport::sample_set_max_hz(int p_hz){
+
+ sample_action_max_hz=p_hz;
+}
+int EditorImportExport::sample_get_max_hz() const{
+
+ return sample_action_max_hz;
+}
+
+void EditorImportExport::sample_set_trim(bool p_trim){
+
+ sample_action_trim=p_trim;
+}
+bool EditorImportExport::sample_get_trim() const{
+
+ return sample_action_trim;
+}
+
+
void EditorImportExport::_bind_methods() {
ObjectTypeDB::bind_method(_MD("image_export_group_create"),&EditorImportExport::image_export_group_create);
@@ -1714,8 +1928,21 @@ EditorImportExport::EditorImportExport() {
image_formats.insert("png");
image_shrink=1;
+
script_action=SCRIPT_ACTION_COMPILE;
+ sample_action=SAMPLE_ACTION_NONE;
+ sample_action_max_hz=44100;
+ sample_action_trim=false;
+
+}
+
+
+
+EditorImportExport::~EditorImportExport() {
+
+
+
}
diff --git a/tools/editor/editor_import_export.h b/tools/editor/editor_import_export.h
index 8305e3c88c..1a3171e66b 100644
--- a/tools/editor/editor_import_export.h
+++ b/tools/editor/editor_import_export.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -83,6 +83,7 @@ public:
typedef Error (*EditorExportSaveFunction)(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total);
protected:
+ Vector<uint8_t> get_exported_file_default(String& p_fname) const;
virtual Vector<uint8_t> get_exported_file(String& p_fname) const;
virtual Vector<StringName> get_dependencies(bool p_bundles) const;
@@ -100,9 +101,11 @@ protected:
Vector<TempData> file_ofs;
EditorProgress *ep;
int count;
+ int alignment;
};
+ void gen_export_flags(Vector<String> &r_flags, int p_flags);
static Error save_pack_file(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total);
public:
@@ -118,10 +121,17 @@ public:
IMAGE_COMPRESSION_ETC2, // ericsson new compression format (can handle alpha)
};
+ enum ExportFlags {
+ EXPORT_DUMB_CLIENT=1,
+ EXPORT_REMOTE_DEBUG=2,
+ EXPORT_VIEW_COLLISONS=4,
+ EXPORT_VIEW_NAVIGATION=8
+ };
+
Error export_project_files(EditorExportSaveFunction p_func, void* p_udata,bool p_make_bundles);
- Error save_pack(FileAccess *p_where, bool p_make_bundles=false);
+ Error save_pack(FileAccess *p_where, bool p_make_bundles=false, int p_alignment = 1);
virtual String get_name() const =0;
virtual ImageCompression get_image_compression() const=0;
virtual Ref<Texture> get_logo() const =0;
@@ -130,14 +140,14 @@ public:
virtual int get_device_count() const { return 0; }
virtual String get_device_name(int p_device) const { return ""; }
virtual String get_device_info(int p_device) const { return ""; }
- virtual Error run(int p_device,bool p_dumb=false) { return OK; }
+ virtual Error run(int p_device,int p_flags) { return OK; }
virtual bool can_export(String *r_error=NULL) const=0;
virtual bool requieres_password(bool p_debug) const { return false; }
virtual String get_binary_extension() const=0;
- virtual Error export_project(const String& p_path,bool p_debug,bool p_dumb=false)=0;
+ virtual Error export_project(const String& p_path,bool p_debug,int p_flags=0)=0;
EditorExportPlatform() {};
};
@@ -187,7 +197,7 @@ public:
virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_BC; }
virtual String get_binary_extension() const { return binary_extension; }
- virtual Error export_project(const String& p_path,bool p_debug,bool p_dumb=false);
+ virtual Error export_project(const String& p_path, bool p_debug, int p_flags=0);
virtual void set_release_binary32(const String& p_binary) { release_binary32=p_binary; }
virtual void set_debug_binary32(const String& p_binary) { debug_binary32=p_binary; }
virtual void set_release_binary64(const String& p_binary) { release_binary64=p_binary; }
@@ -225,6 +235,8 @@ public:
IMAGE_ACTION_NONE,
IMAGE_ACTION_COMPRESS_DISK,
IMAGE_ACTION_COMPRESS_RAM,
+ IMAGE_ACTION_KEEP //for group
+
};
enum ScriptAction {
@@ -233,6 +245,12 @@ public:
SCRIPT_ACTION_ENCRYPT
};
+ enum SampleAction {
+
+ SAMPLE_ACTION_NONE,
+ SAMPLE_ACTION_COMPRESS_RAM,
+ };
+
protected:
struct ImageGroup {
@@ -240,7 +258,7 @@ protected:
ImageAction action;
bool make_atlas;
float lossy_quality;
- int shrink;
+ float shrink;
};
Vector<Ref<EditorExportPlugin> > export_plugins;
@@ -248,7 +266,7 @@ protected:
Map<String,int> by_idx;
ImageAction image_action;
float image_action_compress_quality;
- int image_shrink;
+ float image_shrink;
Set<String> image_formats;
ExportFilter export_filter;
@@ -262,6 +280,10 @@ protected:
ScriptAction script_action;
String script_key;
+ SampleAction sample_action;
+ int sample_action_max_hz;
+ bool sample_action_trim;
+
static EditorImportExport* singleton;
static void _bind_methods();
@@ -270,6 +292,7 @@ public:
static EditorImportExport* get_singleton() { return singleton; }
void add_import_plugin(const Ref<EditorImportPlugin>& p_plugin);
+ void remove_import_plugin(const Ref<EditorImportPlugin>& p_plugin);
int get_import_plugin_count() const;
Ref<EditorImportPlugin> get_import_plugin(int p_idx) const;
Ref<EditorImportPlugin> get_import_plugin_by_name(const String& p_string) const;
@@ -297,8 +320,8 @@ public:
void set_export_image_action(ImageAction p_action);
ImageAction get_export_image_action() const;
- void set_export_image_shrink(int p_shrink);
- int get_export_image_shrink() const;
+ void set_export_image_shrink(float p_shrink);
+ float get_export_image_shrink() const;
void set_export_image_quality(float p_quality);
float get_export_image_quality() const;
@@ -313,8 +336,8 @@ public:
ImageAction image_export_group_get_image_action(const StringName& p_export_group) const;
void image_export_group_set_make_atlas(const StringName& p_export_group,bool p_make);
bool image_export_group_get_make_atlas(const StringName& p_export_group) const;
- void image_export_group_set_shrink(const StringName& p_export_group,int p_amount);
- int image_export_group_get_shrink(const StringName& p_export_group) const;
+ void image_export_group_set_shrink(const StringName& p_export_group,float p_amount);
+ float image_export_group_get_shrink(const StringName& p_export_group) const;
void image_export_group_set_lossy_quality(const StringName& p_export_group,float p_quality);
float image_export_group_get_lossy_quality(const StringName& p_export_group) const;
@@ -330,10 +353,20 @@ public:
void script_set_encryption_key(const String& p_key);
String script_get_encryption_key() const;
+ void sample_set_action(SampleAction p_action);
+ SampleAction sample_get_action() const;
+
+ void sample_set_max_hz(int p_hz);
+ int sample_get_max_hz() const;
+
+ void sample_set_trim(bool p_trim);
+ bool sample_get_trim() const;
+
void load_config();
void save_config();
EditorImportExport();
+ ~EditorImportExport();
};
VARIANT_ENUM_CAST(EditorImportExport::ImageAction);
diff --git a/tools/editor/editor_log.cpp b/tools/editor/editor_log.cpp
index f7f5f596d7..264117eecd 100644
--- a/tools/editor/editor_log.cpp
+++ b/tools/editor/editor_log.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -81,6 +81,7 @@ void EditorLog::_notification(int p_what) {
log->add_color_override("default_color",get_color("font_color","Tree"));
tb->set_normal_texture( get_icon("Collapse","EditorIcons"));
tb->set_hover_texture( get_icon("CollapseHl","EditorIcons"));
+ //button->set_icon(get_icon("Console","EditorIcons"));
}
@@ -104,6 +105,17 @@ void EditorLog::_close_request() {
}
+void EditorLog::_clear_request() {
+
+ log->clear();
+
+}
+
+void EditorLog::clear() {
+ _clear_request();
+}
+
+
void EditorLog::add_message(const String& p_msg,bool p_error) {
@@ -114,6 +126,7 @@ void EditorLog::add_message(const String& p_msg,bool p_error) {
log->push_color(get_color("fg_error","Editor"));
} else {
button->set_icon(Ref<Texture>());
+
}
@@ -143,17 +156,20 @@ void EditorLog::_dragged(const Point2& p_ofs) {
*/
-ToolButton *EditorLog::get_button() {
+Button *EditorLog::get_button() {
return button;
}
void EditorLog::_flip_request() {
- if (is_visible())
+ if (is_visible()) {
hide();
- else
+ button->show();
+ } else {
show();
+ button->hide();
+ }
}
void EditorLog::_undo_redo_cbk(void *p_self,const String& p_name) {
@@ -167,9 +183,12 @@ void EditorLog::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_close_request"),&EditorLog::_close_request );
ObjectTypeDB::bind_method(_MD("_flip_request"),&EditorLog::_flip_request );
+ ObjectTypeDB::bind_method(_MD("_clear_request"),&EditorLog::_clear_request );
+
//ObjectTypeDB::bind_method(_MD("_dragged"),&EditorLog::_dragged );
ADD_SIGNAL( MethodInfo("close_request"));
ADD_SIGNAL( MethodInfo("show_request"));
+ ADD_SIGNAL( MethodInfo("clear_request"));
}
EditorLog::EditorLog() {
@@ -186,7 +205,7 @@ EditorLog::EditorLog() {
hb->add_child(title);
- button = memnew( ToolButton );
+ button = memnew( Button );
button->set_text_align(Button::ALIGN_LEFT);
button->connect("pressed",this,"_flip_request");
button->set_focus_mode(FOCUS_NONE);
@@ -198,14 +217,19 @@ EditorLog::EditorLog() {
//pd->connect("dragged",this,"_dragged");
//pd->set_default_cursor_shape(Control::CURSOR_MOVE);
+ clearbutton = memnew( Button );
+ hb->add_child(clearbutton);
+ clearbutton->set_text("Clear");
+ clearbutton->connect("pressed", this,"_clear_request");
+
tb = memnew( TextureButton );
hb->add_child(tb);
tb->connect("pressed",this,"_close_request");
- ec = memnew( EmptyControl);
+ ec = memnew( Control);
vb->add_child(ec);
- ec->set_minsize(Size2(0,100));
+ ec->set_custom_minimum_size(Size2(0,100));
ec->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -219,7 +243,7 @@ EditorLog::EditorLog() {
log->set_selection_enabled(true);
log->set_focus_mode(FOCUS_CLICK);
pc->add_child(log);
- add_message(VERSION_FULL_NAME" (c) 2008-2014 Juan Linietsky, Ariel Manzur.");
+ add_message(VERSION_FULL_NAME" (c) 2008-2015 Juan Linietsky, Ariel Manzur.");
//log->add_text("Initialization Complete.\n"); //because it looks cool.
add_style_override("panel",get_stylebox("panelf","Panel"));
@@ -241,8 +265,8 @@ void EditorLog::deinit() {
}
+
EditorLog::~EditorLog() {
}
-
diff --git a/tools/editor/editor_log.h b/tools/editor/editor_log.h
index b6cd951287..93044f9a2d 100644
--- a/tools/editor/editor_log.h
+++ b/tools/editor/editor_log.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,7 +33,7 @@
#include "scene/gui/label.h"
#include "scene/gui/rich_text_label.h"
#include "scene/gui/texture_button.h"
-#include "scene/gui/empty_control.h"
+//#include "scene/gui/empty_control.h"
#include "scene/gui/box_container.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/texture_frame.h"
@@ -44,13 +44,14 @@ class EditorLog : public PanelContainer {
OBJ_TYPE( EditorLog, PanelContainer );
- ToolButton *button;
+ Button *button;
+ Button *clearbutton;
Label *title;
RichTextLabel *log;
TextureButton *tb;
HBoxContainer *title_hb;
// PaneDrag *pd;
- EmptyControl *ec;
+ Control *ec;
static void _error_handler(void *p_self, const char*p_func, const char*p_file,int p_line, const char*p_error,const char*p_errorexp,ErrorHandlerType p_type);
@@ -58,10 +59,10 @@ class EditorLog : public PanelContainer {
Thread::ID current;
-
// void _dragged(const Point2& p_ofs);
void _close_request();
void _flip_request();
+ void _clear_request();
static void _undo_redo_cbk(void *p_self,const String& p_name);
protected:
@@ -72,7 +73,8 @@ public:
void add_message(const String& p_msg, bool p_error=false);
void deinit();
- ToolButton *get_button();
+ Button *get_button();
+ void clear();
EditorLog();
~EditorLog();
};
diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp
index dbc896cbb9..4e22592880 100644
--- a/tools/editor/editor_node.cpp
+++ b/tools/editor/editor_node.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -54,10 +54,11 @@
#include "register_exporters.h"
#include "bind/core_bind.h"
#include "io/zip_io.h"
-
+#include "io/config_file.h"
// plugins
#include "plugins/sprite_frames_editor_plugin.h"
+#include "plugins/sprite_region_editor_plugin.h"
#include "plugins/canvas_item_editor_plugin.h"
#include "plugins/spatial_editor_plugin.h"
#include "plugins/sample_editor_plugin.h"
@@ -75,6 +76,7 @@
#include "plugins/tile_map_editor_plugin.h"
#include "plugins/cube_grid_theme_editor_plugin.h"
#include "plugins/shader_editor_plugin.h"
+#include "plugins/shader_graph_editor_plugin.h"
#include "plugins/path_editor_plugin.h"
#include "plugins/rich_text_editor_plugin.h"
#include "plugins/collision_polygon_editor_plugin.h"
@@ -88,6 +90,11 @@
#include "plugins/animation_player_editor_plugin.h"
#include "plugins/baked_light_editor_plugin.h"
#include "plugins/polygon_2d_editor_plugin.h"
+#include "plugins/navigation_polygon_editor_plugin.h"
+#include "plugins/light_occluder_2d_editor_plugin.h"
+#include "plugins/color_ramp_editor_plugin.h"
+#include "plugins/collision_shape_2d_editor_plugin.h"
+#include "main/input_default.h"
// end
#include "tools/editor/io_plugins/editor_texture_import_plugin.h"
#include "tools/editor/io_plugins/editor_scene_import_plugin.h"
@@ -96,15 +103,53 @@
#include "tools/editor/io_plugins/editor_translation_import_plugin.h"
#include "tools/editor/io_plugins/editor_mesh_import_plugin.h"
+#include "plugins/editor_preview_plugins.h"
+#include "script_editor_debugger.h"
EditorNode *EditorNode::singleton=NULL;
+void EditorNode::_update_scene_tabs() {
+
+ bool show_rb = EditorSettings::get_singleton()->get("global/show_script_in_scene_tabs");
+
+ scene_tabs->clear_tabs();
+ Ref<Texture> script_icon = gui_base->get_icon("Script","EditorIcons");
+ for(int i=0;i<editor_data.get_edited_scene_count();i++) {
+
+ String type=editor_data.get_scene_type(i);
+ Ref<Texture> icon;
+ if (type!=String()) {
+
+ if (!gui_base->has_icon(type,"EditorIcons")) {
+ type="Node";
+ }
+
+ icon=gui_base->get_icon(type,"EditorIcons");
+
+ }
+
+
+
+ int current = editor_data.get_edited_scene();
+ bool unsaved = (i==current)?saved_version!=editor_data.get_undo_redo().get_version():editor_data.get_scene_version(i)!=0;
+ scene_tabs->add_tab(editor_data.get_scene_title(i)+(unsaved?"(*)":""),icon);
+
+ if (show_rb && editor_data.get_scene_root_script(i).is_valid()) {
+ scene_tabs->set_tab_right_button(i,script_icon);
+ }
+
+ }
+
+ scene_tabs->set_current_tab(editor_data.get_edited_scene());
+
+}
+
void EditorNode::_update_title() {
String appname = Globals::get_singleton()->get("application/name");
String title = appname.empty()?String(VERSION_FULL_NAME):String(_MKSTR(VERSION_NAME) + String(" - ") + appname);
- String edited = edited_scene?edited_scene->get_filename():String();
+ String edited = editor_data.get_edited_scene_root()?editor_data.get_edited_scene_root()->get_filename():String();
if (!edited.empty())
title+=" - " + String(edited.get_file());
if (unsaved_cache)
@@ -120,13 +165,13 @@ void EditorNode::_unhandled_input(const InputEvent& p_event) {
switch(p_event.key.scancode) {
- case KEY_F1:
+ /*case KEY_F1:
if (!p_event.key.mod.shift && !p_event.key.mod.command)
_editor_select(3);
- break;
- case KEY_F2: _editor_select(0); break;
- case KEY_F3: _editor_select(1); break;
- case KEY_F4: _editor_select(2); break;
+ break;*/
+ case KEY_F1: _editor_select(0); break;
+ case KEY_F2: _editor_select(1); break;
+ case KEY_F3: _editor_select(2); break;
case KEY_F5: _menu_option_confirm((p_event.key.mod.control&&p_event.key.mod.shift)?RUN_PLAY_CUSTOM_SCENE:RUN_PLAY,true); break;
case KEY_F6: _menu_option_confirm(RUN_PLAY_SCENE,true); break;
case KEY_F7: _menu_option_confirm(RUN_PAUSE,true); break;
@@ -141,6 +186,7 @@ void EditorNode::_unhandled_input(const InputEvent& p_event) {
void EditorNode::_notification(int p_what) {
if (p_what==NOTIFICATION_EXIT_TREE) {
+
editor_data.save_editor_external_data();
log->deinit(); // do not get messages anymore
@@ -168,6 +214,11 @@ void EditorNode::_notification(int p_what) {
_update_title();
}
+ if (last_checked_version!=editor_data.get_undo_redo().get_version()) {
+ _update_scene_tabs();
+ last_checked_version=editor_data.get_undo_redo().get_version();
+ }
+
//get_root_node()->set_rect(viewport->get_global_rect());
//update the circle
@@ -181,7 +232,7 @@ void EditorNode::_notification(int p_what) {
circle_step=0;
circle_step_msec=tick;
- circle_step_frame=frame+1;
+ circle_step_frame=frame+1;
update_menu->set_icon(gui_base->get_icon("Progress"+itos(circle_step+1),"EditorIcons"));
@@ -215,6 +266,7 @@ void EditorNode::_notification(int p_what) {
}
if (p_what==NOTIFICATION_ENTER_TREE) {
+
//MessageQueue::get_singleton()->push_call(this,"_get_scene_metadata");
get_tree()->set_editor_hint(true);
get_tree()->get_root()->set_as_audio_listener(false);
@@ -224,10 +276,19 @@ void EditorNode::_notification(int p_what) {
//import_monitor->scan_changes();
}
+
+ if (p_what==NOTIFICATION_EXIT_TREE) {
+
+
+ editor_data.clear_edited_scenes();
+
+ }
if (p_what==NOTIFICATION_READY) {
VisualServer::get_singleton()->viewport_set_hide_scenario(get_scene_root()->get_viewport(),true);
VisualServer::get_singleton()->viewport_set_hide_canvas(get_scene_root()->get_viewport(),true);
+ VisualServer::get_singleton()->viewport_set_disable_environment(get_viewport()->get_viewport_rid(),true);
+
_editor_select(1);
if (defer_load_scene!="") {
@@ -313,6 +374,11 @@ void EditorNode::_fs_changed() {
E->get()->invalidate();
}
+
+ for(Set<EditorFileDialog*>::Element *E=editor_file_dialogs.front();E;E=E->next()) {
+
+ E->get()->invalidate();
+ }
}
void EditorNode::_sources_changed(bool p_exist) {
@@ -335,6 +401,19 @@ void EditorNode::_vp_resized() {
}
+void EditorNode::_rebuild_import_menu()
+{
+ PopupMenu* p = import_menu->get_popup();
+ p->clear();
+ p->add_item("Sub-Scene", FILE_IMPORT_SUBSCENE);
+ p->add_separator();
+ for (int i = 0; i < editor_import_export->get_import_plugin_count(); i++) {
+ p->add_item(editor_import_export->get_import_plugin(i)->get_visible_name(), IMPORT_PLUGIN_BASE + i);
+ }
+ p->add_separator();
+ p->add_item("Re-Import..", SETTINGS_IMPORT);
+}
+
void EditorNode::_node_renamed() {
if (property_editor)
@@ -366,7 +445,7 @@ void EditorNode::edit_node(Node *p_node) {
void EditorNode::open_resource(const String& p_type) {
- file->set_mode(FileDialog::MODE_OPEN_FILE);
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type(p_type,&extensions);
@@ -383,17 +462,74 @@ void EditorNode::open_resource(const String& p_type) {
current_option=RESOURCE_LOAD;
}
-void EditorNode::save_resource(const Ref<Resource>& p_resource) {
+void EditorNode::save_resource_in_path(const Ref<Resource>& p_resource,const String& p_path) {
+
+ editor_data.apply_changes_in_editors();
+ int flg=0;
+ if (EditorSettings::get_singleton()->get("on_save/compress_binary_resources"))
+ flg|=ResourceSaver::FLAG_COMPRESS;
+ if (EditorSettings::get_singleton()->get("on_save/save_paths_as_relative"))
+ flg|=ResourceSaver::FLAG_RELATIVE_PATHS;
+
+ String path = Globals::get_singleton()->localize_path(p_path);
+ Error err = ResourceSaver::save(path,p_resource,flg|ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS);
+
+ if (err!=OK) {
+ accept->set_text("Error saving resource!");
+ accept->popup_centered_minsize();
+ return;
+ }
+// EditorFileSystem::get_singleton()->update_file(path,p_resource->get_type());
+
+ ((Resource*)p_resource.ptr())->set_path(path);
+ emit_signal("resource_saved",p_resource);
- ERR_FAIL_COND(p_resource.is_null() || p_resource->get_path()=="" || p_resource->get_path().find("::")!=-1);
- resources_dock->save_resource(p_resource->get_path(),p_resource);
+}
+
+void EditorNode::save_resource(const Ref<Resource>& p_resource) {
+
+ if (p_resource->get_path().is_resource_file()) {
+ save_resource_in_path(p_resource,p_resource->get_path());
+ } else {
+ save_resource_as(p_resource);
+ }
}
void EditorNode::save_resource_as(const Ref<Resource>& p_resource) {
- resources_dock->save_resource_as(p_resource);
+ file->set_mode(EditorFileDialog::MODE_SAVE_FILE);
+ bool relpaths = (p_resource->has_meta("__editor_relpaths__") && p_resource->get_meta("__editor_relpaths__").operator bool());
+
+ List<String> extensions;
+ Ref<PackedScene> sd = memnew( PackedScene );
+ ResourceSaver::get_recognized_extensions(p_resource,&extensions);
+ file->clear_filters();
+ for(int i=0;i<extensions.size();i++) {
+ file->add_filter("*."+extensions[i]+" ; "+extensions[i].to_upper());
+ }
+
+ //file->set_current_path(current_path);
+ if (p_resource->get_path()!="") {
+ file->set_current_path(p_resource->get_path());
+ if (extensions.size()) {
+ String ext=p_resource->get_path().extension().to_lower();
+ if (extensions.find(ext)==NULL) {
+ file->set_current_path(p_resource->get_path().replacen("."+ext,"."+extensions.front()->get()));
+ }
+ }
+ } else {
+
+ String existing;
+ if (extensions.size()) {
+ existing="new_"+p_resource->get_type().to_lower()+"."+extensions.front()->get().to_lower();
+ }
+ file->set_current_path(existing);
+
+ }
+ file->popup_centered_ratio();
+ file->set_title("Save Resource As..");
}
@@ -434,14 +570,14 @@ void EditorNode::_dialog_display_file_error(String p_file,Error p_error) {
}break;
}
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
}
}
void EditorNode::_get_scene_metadata() {
- Node *scene = edited_scene;
+ Node *scene = editor_data.get_edited_scene_root();
if (!scene)
return;
@@ -468,7 +604,7 @@ void EditorNode::_get_scene_metadata() {
void EditorNode::_set_scene_metadata() {
- Node *scene = edited_scene;
+ Node *scene = editor_data.get_edited_scene_root();
if (!scene)
return;
@@ -590,7 +726,42 @@ static Error _fix_imported_scene_paths(Node* node, Node* root, String save_path)
};
-bool EditorNode::_find_and_save_edited_subresources(Object *obj,Set<RES>& processed,int32_t flags) {
+bool EditorNode::_find_and_save_resource(RES res,Map<RES,bool>& processed,int32_t flags) {
+
+ if (res.is_null())
+ return false;
+
+ if (processed.has(res)) {
+
+ return processed[res];
+ }
+
+
+ bool changed = res->is_edited();
+ res->set_edited(false);
+
+ bool subchanged = _find_and_save_edited_subresources(res.ptr(),processed,flags);
+
+// print_line("checking if edited: "+res->get_type()+" :: "+res->get_name()+" :: "+res->get_path()+" :: "+itos(changed)+" :: SR "+itos(subchanged));
+
+ if (res->get_path().is_resource_file()) {
+ if (changed || subchanged) {
+ //save
+ print_line("Also saving modified external resource: "+res->get_path());
+ Error err = ResourceSaver::save(res->get_path(),res,flags);
+
+ }
+ processed[res]=false; //because it's a file
+ return false;
+ } else {
+
+
+ processed[res]=changed;
+ return changed;
+ }
+}
+
+bool EditorNode::_find_and_save_edited_subresources(Object *obj,Map<RES,bool>& processed,int32_t flags) {
bool ret_changed=false;
List<PropertyInfo> pi;
@@ -600,57 +771,45 @@ bool EditorNode::_find_and_save_edited_subresources(Object *obj,Set<RES>& proces
if (!(E->get().usage&PROPERTY_USAGE_STORAGE))
continue;
+
+
switch(E->get().type) {
case Variant::OBJECT: {
RES res = obj->get(E->get().name);
- if (res.is_null() || processed.has(res))
- break;
-
- processed.insert(res);
-
- bool changed = res->is_edited();
- res->set_edited(false);
-
- bool subchanged = _find_and_save_edited_subresources(res.ptr(),processed,flags);
-
- if (res->get_path().is_resource_file()) {
- if (changed || subchanged) {
- //save
- print_line("Also saving modified external resource: "+res->get_path());
- Error err = ResourceSaver::save(res->get_path(),res,flags);
-
- }
- } else {
-
+ if (_find_and_save_resource(res,processed,flags))
ret_changed=true;
- }
-
} break;
case Variant::ARRAY: {
- /*Array varray=p_variant;
+ Array varray= obj->get(E->get().name);
int len=varray.size();
for(int i=0;i<len;i++) {
Variant v=varray.get(i);
- _find_resources(v);
- }*/
+ RES res=v;
+ if (_find_and_save_resource(res,processed,flags))
+ ret_changed=true;
+
+ //_find_resources(v);
+ }
} break;
case Variant::DICTIONARY: {
- /*
- Dictionary d=p_variant;
+
+ Dictionary d=obj->get(E->get().name);;
List<Variant> keys;
d.get_key_list(&keys);
for(List<Variant>::Element *E=keys.front();E;E=E->next()) {
Variant v = d[E->get()];
- _find_resources(v);
- } */
+ RES res=v;
+ if (_find_and_save_resource(res,processed,flags))
+ ret_changed=true;
+ }
} break;
default: {}
}
@@ -661,23 +820,113 @@ bool EditorNode::_find_and_save_edited_subresources(Object *obj,Set<RES>& proces
}
-void EditorNode::_save_edited_subresources(Node* scene,Set<RES>& processed,int32_t flags) {
+void EditorNode::_save_edited_subresources(Node* scene,Map<RES,bool>& processed,int32_t flags) {
_find_and_save_edited_subresources(scene,processed,flags);
for(int i=0;i<scene->get_child_count();i++) {
Node *n = scene->get_child(i);
- if (n->get_owner()!=edited_scene)
+ if (n->get_owner()!=editor_data.get_edited_scene_root())
continue;
_save_edited_subresources(n,processed,flags);
}
}
+void EditorNode::_find_node_types(Node* p_node, int&count_2d, int&count_3d) {
+
+ if (p_node->is_type("Viewport") || (p_node!=editor_data.get_edited_scene_root() && p_node->get_owner()!=editor_data.get_edited_scene_root()))
+ return;
+
+ if (p_node->is_type("CanvasItem"))
+ count_2d++;
+ else if (p_node->is_type("Spatial"))
+ count_3d++;
+
+ for(int i=0;i<p_node->get_child_count();i++)
+ _find_node_types(p_node->get_child(i),count_2d,count_3d);
+
+}
+
+
+void EditorNode::_save_scene_with_preview(String p_file) {
+
+ int c2d=0;
+ int c3d=0;
+
+ EditorProgress save("save","Saving Scene",4);
+ save.step("Analyzing",0);
+ _find_node_types(editor_data.get_edited_scene_root(),c2d,c3d);
+
+ RID viewport;
+ bool is2d;
+ if (c3d<c2d) {
+ viewport=scene_root->get_viewport();
+ is2d=true;
+ } else {
+ viewport=SpatialEditor::get_singleton()->get_editor_viewport(0)->get_viewport_node()->get_viewport();
+ is2d=false;
+
+ }
+ save.step("Creating Thumbnail",1);
+ //current view?
+ int screen =-1;
+ for(int i=0;i<editor_table.size();i++) {
+ if (editor_plugin_screen==editor_table[i]) {
+ screen=i;
+ break;
+ }
+ }
+
+ _editor_select(is2d?0:1);
+
+ VS::get_singleton()->viewport_queue_screen_capture(viewport);
+ save.step("Creating Thumbnail",2);
+ save.step("Creating Thumbnail",3);
+ Image img = VS::get_singleton()->viewport_get_screen_capture(viewport);
+ int preview_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");;
+ int width,height;
+ if (img.get_width() > preview_size && img.get_width() >= img.get_height()) {
+
+ width=preview_size;
+ height = img.get_height() * preview_size / img.get_width();
+ } else if (img.get_height() > preview_size && img.get_height() >= img.get_width()) {
+
+ height=preview_size;
+ width = img.get_width() * preview_size / img.get_height();
+ } else {
+
+ width=img.get_width();
+ height=img.get_height();
+ }
+
+ img.convert(Image::FORMAT_RGB);
+ img.resize(width,height);
+
+ String pfile = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp/last_scene_preview.png");
+ img.save_png(pfile);
+ Vector<uint8_t> imgdata = FileAccess::get_file_as_array(pfile);
+
+ //print_line("img data is "+itos(imgdata.size()));
+
+ if (editor_data.get_edited_scene_import_metadata().is_null())
+ editor_data.set_edited_scene_import_metadata(Ref<ResourceImportMetadata>( memnew( ResourceImportMetadata ) ) );
+ editor_data.get_edited_scene_import_metadata()->set_option("thumbnail",imgdata);
+
+ //tamanio tel thumbnail
+ if (screen!=-1) {
+ _editor_select(screen);
+ }
+ save.step("Saving Scene",4);
+ _save_scene(p_file);
+
+}
+
+
void EditorNode::_save_scene(String p_file) {
- Node *scene = edited_scene;
+ Node *scene = editor_data.get_edited_scene_root();
if (!scene) {
@@ -685,7 +934,7 @@ void EditorNode::_save_scene(String p_file) {
//accept->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("This operation can't be done without a tree root.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
return;
}
@@ -707,11 +956,11 @@ void EditorNode::_save_scene(String p_file) {
//accept->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("Couldn't save scene. Likely dependencies (instances) couldn't be satisfied.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
return;
}
- sdata->set_import_metadata(scene_import_metadata);
+ sdata->set_import_metadata(editor_data.get_edited_scene_import_metadata());
int flg=0;
if (EditorSettings::get_singleton()->get("on_save/compress_binary_resources"))
flg|=ResourceSaver::FLAG_COMPRESS;
@@ -721,14 +970,15 @@ void EditorNode::_save_scene(String p_file) {
err = ResourceSaver::save(p_file,sdata,flg);
- Set<RES> processed;
+ Map<RES,bool> processed;
_save_edited_subresources(scene,processed,flg);
editor_data.save_editor_external_data();
if (err==OK) {
scene->set_filename( Globals::get_singleton()->localize_path(p_file) );
//EditorFileSystem::get_singleton()->update_file(p_file,sdata->get_type());
- saved_version=editor_data.get_undo_redo().get_version();
+ set_current_version(editor_data.get_undo_redo().get_version());
_update_title();
+ _update_scene_tabs();
} else {
_dialog_display_file_error(p_file,err);
@@ -860,6 +1110,11 @@ void EditorNode::_dialog_action(String p_file) {
push_item(res.operator->() );
+ } break;
+ case FILE_NEW_INHERITED_SCENE: {
+
+
+ load_scene(p_file,false,true);
} break;
case FILE_OPEN_SCENE: {
@@ -908,14 +1163,14 @@ void EditorNode::_dialog_action(String p_file) {
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("This operation requieres a single selected node.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
Node *base = selection.front()->get();
Map<Node*,Node*> reown;
- reown[get_edited_scene()]=base;
+ reown[editor_data.get_edited_scene_root()]=base;
Node *copy = base->duplicate_and_reown(reown);
if (copy) {
@@ -930,7 +1185,7 @@ void EditorNode::_dialog_action(String p_file) {
//accept->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("Couldn't save subscene. Likely dependencies (instances) couldn't be satisfied.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
return;
}
@@ -948,7 +1203,7 @@ void EditorNode::_dialog_action(String p_file) {
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("Error saving scene.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
//EditorFileSystem::get_singleton()->update_file(p_file,sdata->get_type());
@@ -959,7 +1214,7 @@ void EditorNode::_dialog_action(String p_file) {
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("Error duplicating scene to save it.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
@@ -973,10 +1228,22 @@ void EditorNode::_dialog_action(String p_file) {
if (file->get_mode()==FileDialog::MODE_SAVE_FILE) {
- _save_scene(p_file);
+ //_save_scene(p_file);
+ _save_scene_with_preview(p_file);
+
}
} break;
+
+ case FILE_SAVE_AND_RUN: {
+ if (file->get_mode()==FileDialog::MODE_SAVE_FILE) {
+
+ //_save_scene(p_file);
+ _save_scene_with_preview(p_file);
+ _run(false);
+ }
+ } break;
+
case FILE_EXPORT_MESH_LIBRARY: {
Ref<MeshLibrary> ml;
@@ -988,7 +1255,7 @@ void EditorNode::_dialog_action(String p_file) {
//accept->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("Can't load MeshLibrary for merging!.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
return;
}
@@ -998,14 +1265,14 @@ void EditorNode::_dialog_action(String p_file) {
ml = Ref<MeshLibrary>( memnew( MeshLibrary ));
}
- MeshLibraryEditor::update_library_file(edited_scene,ml,true);
+ MeshLibraryEditor::update_library_file(editor_data.get_edited_scene_root(),ml,true);
Error err = ResourceSaver::save(p_file,ml);
if (err) {
accept->get_ok()->set_text("I see..");
accept->set_text("Error saving MeshLibrary!.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
return;
}
@@ -1022,7 +1289,7 @@ void EditorNode::_dialog_action(String p_file) {
//accept->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("Can't load TileSet for merging!.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
return;
}
@@ -1032,14 +1299,14 @@ void EditorNode::_dialog_action(String p_file) {
ml = Ref<TileSet>( memnew( TileSet ));
}
- TileSetEditor::update_library_file(edited_scene,ml,true);
+ TileSetEditor::update_library_file(editor_data.get_edited_scene_root(),ml,true);
Error err = ResourceSaver::save(p_file,ml);
if (err) {
accept->get_ok()->set_text("I see..");
accept->set_text("Error saving TileSet!.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
return;
}
} break;
@@ -1056,7 +1323,7 @@ void EditorNode::_dialog_action(String p_file) {
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("Can't open export templates zip.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
return;
}
@@ -1119,13 +1386,26 @@ void EditorNode::_dialog_action(String p_file) {
unzClose(pkg);
} break;
+ case RESOURCE_SAVE:
+ case RESOURCE_SAVE_AS: {
+
+
+ uint32_t current = editor_history.get_current();
+ Object *current_obj = current>0 ? ObjectDB::get_instance(current) : NULL;
+
+ ERR_FAIL_COND(!current_obj->cast_to<Resource>())
+ RES current_res = RES(current_obj->cast_to<Resource>());
+
+ save_resource_in_path(current_res,p_file);
+
+ } break;
default: { //save scene?
-
if (file->get_mode()==FileDialog::MODE_SAVE_FILE) {
- _save_scene(p_file);
+ //_save_scene(p_file);
+ _save_scene_with_preview(p_file);
}
} break;
@@ -1156,6 +1436,67 @@ void EditorNode::push_item(Object *p_object,const String& p_property) {
}
+void EditorNode::_select_history(int p_idx) {
+
+ //push it to the top, it is not correct, but it's more useful
+ ObjectID id=editor_history.get_history_obj(p_idx);
+ Object* obj=ObjectDB::get_instance(id);
+ if (!obj)
+ return;
+ push_item(obj);
+}
+
+void EditorNode::_prepare_history() {
+
+ int history_to = MAX(0,editor_history.get_history_len()-25);
+
+ editor_history_menu->get_popup()->clear();
+
+ Ref<Texture> base_icon = gui_base->get_icon("Object","EditorIcons");
+ Set<ObjectID> already;
+ for(int i=editor_history.get_history_len()-1;i>=history_to;i--) {
+
+
+ ObjectID id=editor_history.get_history_obj(i);
+ Object* obj=ObjectDB::get_instance(id);
+ if (!obj || already.has(id)) {
+ if (history_to>0) {
+ history_to--;
+ }
+ continue;
+ }
+
+ already.insert(id);
+
+ Ref<Texture> icon = gui_base->get_icon("Object","EditorIcons");
+ if (gui_base->has_icon(obj->get_type(),"EditorIcons"))
+ icon=gui_base->get_icon(obj->get_type(),"EditorIcons");
+ else
+ icon=base_icon;
+
+ String text;
+ if (obj->cast_to<Resource>()) {
+ Resource *r=obj->cast_to<Resource>();
+ if (r->get_path().is_resource_file())
+ text=r->get_path().get_file();
+ else if (r->get_name()!=String()) {
+ text=r->get_name();
+ } else {
+ text=r->get_type();
+ }
+ } else if (obj->cast_to<Node>()) {
+ text=obj->cast_to<Node>()->get_name();
+ } else {
+ text=obj->get_type();
+ }
+
+ if (i==editor_history.get_history_pos()) {
+ text="["+text+"]";
+ }
+ editor_history_menu->get_popup()->add_icon_item(icon,text,i);
+ }
+}
+
void EditorNode::_property_editor_forward() {
if (editor_history.next())
@@ -1172,15 +1513,15 @@ void EditorNode::_property_editor_back() {
void EditorNode::_imported(Node *p_node) {
- Node *scene = edited_scene;
- set_edited_scene(p_node);
-
+ Node *scene = editor_data.get_edited_scene_root();
+// add_edited_scene(p_node);
+/*
if (scene) {
String path = scene->get_filename();
p_node->set_filename(path);
memdelete(scene);
}
-
+*/
}
@@ -1200,6 +1541,9 @@ void EditorNode::_edit_current() {
uint32_t current = editor_history.get_current();
Object *current_obj = current>0 ? ObjectDB::get_instance(current) : NULL;
+ property_back->set_disabled( editor_history.is_at_begining() );
+ property_forward->set_disabled( editor_history.is_at_end() );
+
this->current=current_obj;
editor_path->update_path();
@@ -1214,7 +1558,10 @@ void EditorNode::_edit_current() {
object_menu->set_disabled(true);
- if (current_obj->is_type("Resource")) {
+ bool is_resource = current_obj->is_type("Resource");
+ resource_save_button->set_disabled(!is_resource);
+
+ if (is_resource) {
Resource *current_res = current_obj->cast_to<Resource>();
@@ -1223,12 +1570,11 @@ void EditorNode::_edit_current() {
property_editor->edit( current_res );
object_menu->set_disabled(false);
- resources_dock->add_resource(Ref<Resource>(current_res));
- top_pallete->set_current_tab(1);
- }
+ //resources_dock->add_resource(Ref<Resource>(current_res));
- if (current_obj->is_type("Node")) {
+ //top_pallete->set_current_tab(1);
+ } else if (current_obj->is_type("Node")) {
Node * current_node = current_obj->cast_to<Node>();
ERR_FAIL_COND(!current_node);
@@ -1241,7 +1587,13 @@ void EditorNode::_edit_current() {
scene_tree_dock->set_selected(current_node);
object_menu->get_popup()->clear();
- top_pallete->set_current_tab(0);
+ //top_pallete->set_current_tab(0);
+
+ } else {
+
+ property_editor->edit( current_obj );
+ //scene_tree_dock->set_selected(current_node);
+ //object_menu->get_popup()->clear();
}
@@ -1255,17 +1607,20 @@ void EditorNode::_edit_current() {
if (main_plugin!=editor_plugin_screen) {
// update screen main_plugin
- if (editor_plugin_screen)
- editor_plugin_screen->make_visible(false);
- editor_plugin_screen=main_plugin;
- editor_plugin_screen->edit(current_obj);
- editor_plugin_screen->make_visible(true);
+ if (!changing_scene) {
- for(int i=0;i<editor_table.size();i++) {
- if (editor_table[i]==main_plugin) {
- main_editor_tabs->set_current_tab(i);
- break;
+ if (editor_plugin_screen)
+ editor_plugin_screen->make_visible(false);
+ editor_plugin_screen=main_plugin;
+ editor_plugin_screen->edit(current_obj);
+
+ editor_plugin_screen->make_visible(true);
+
+
+ for(int i=0;i<editor_table.size();i++) {
+
+ main_editor_buttons[i]->set_pressed(editor_table[i]==main_plugin);
}
}
@@ -1310,7 +1665,13 @@ void EditorNode::_edit_current() {
p->add_item("Copy Params",OBJECT_COPY_PARAMS);
p->add_item("Set Params",OBJECT_PASTE_PARAMS);
p->add_separator();
- p->add_item("Make Resources Unique",OBJECT_UNIQUE_RESOURCES);
+ p->add_item("Paste Resource",RESOURCE_PASTE);
+ if (is_resource) {
+ p->add_item("Copy Resource",RESOURCE_COPY);
+ p->add_item("Make Built-In",RESOURCE_UNREF);
+ }
+ p->add_separator();
+ p->add_item("Make Sub-Resources Unique",OBJECT_UNIQUE_RESOURCES);
p->add_separator();
p->add_icon_item(gui_base->get_icon("Help","EditorIcons"),"Class Reference",OBJECT_REQUEST_HELP);
List<MethodInfo> methods;
@@ -1343,6 +1704,20 @@ void EditorNode::_edit_current() {
_update_keying();
}
+void EditorNode::_resource_created() {
+
+ Object *c = create_dialog->instance_selected();
+
+ ERR_FAIL_COND(!c);
+ Resource *r = c->cast_to<Resource>();
+ ERR_FAIL_COND(!r);
+
+ REF res( r );
+
+ push_item(c);
+
+}
+
void EditorNode::_resource_selected(const RES& p_res,const String& p_property) {
@@ -1365,8 +1740,10 @@ void EditorNode::_run(bool p_current,const String& p_custom) {
}
play_button->set_pressed(false);
- pause_button->set_pressed(false);
+ play_button->set_icon(gui_base->get_icon("Play","EditorIcons"));
+ //pause_button->set_pressed(false);
play_scene_button->set_pressed(false);
+ play_scene_button->set_icon(gui_base->get_icon("PlayScene","EditorIcons"));
String current_filename;
String run_filename;
@@ -1374,29 +1751,25 @@ void EditorNode::_run(bool p_current,const String& p_custom) {
- if (p_current || (edited_scene && p_custom==edited_scene->get_filename())) {
+ if (p_current || (editor_data.get_edited_scene_root() && p_custom==editor_data.get_edited_scene_root()->get_filename())) {
- Node *scene = edited_scene;
+ Node *scene = editor_data.get_edited_scene_root();
if (!scene) {
-
current_option=-1;
//accept->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("No scene to run exists.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
return;
}
if (scene->get_filename()=="") {
-
-
current_option=-1;
//accept->get_cancel()->hide();
- accept->get_ok()->set_text("I see..");
- accept->set_text("Scene has never been saved. Save before running!");
- accept->popup_centered(Size2(300,70));;
+ /**/
+ _menu_option_confirm(FILE_SAVE_BEFORE_RUN, false);
return;
}
@@ -1431,7 +1804,7 @@ void EditorNode::_run(bool p_current,const String& p_custom) {
//accept->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("No main scene has ever been defined.\nSelect one from \"Project Settings\" under the 'application' category.");
- accept->popup_centered(Size2(300,100));;
+ accept->popup_centered_minsize();
return;
}
@@ -1442,7 +1815,7 @@ void EditorNode::_run(bool p_current,const String& p_custom) {
if (unsaved_cache) {
- Node *scene = edited_scene;
+ Node *scene = editor_data.get_edited_scene_root();
if (scene) { //only autosave if there is a scene obviously
@@ -1452,17 +1825,22 @@ void EditorNode::_run(bool p_current,const String& p_custom) {
//accept->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("Current scene was never saved, please save scene before running.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
return;
}
- _save_scene(scene->get_filename());
+ //_save_scene(scene->get_filename());
+ _save_scene_with_preview(scene->get_filename());
}
}
editor_data.save_editor_external_data();
}
+ if (bool(EDITOR_DEF("run/always_clear_output_on_play", true))) {
+ log->clear();
+ }
+
List<String> breakpoints;
editor_data.get_editor_breakpoints(&breakpoints);
@@ -1475,7 +1853,7 @@ void EditorNode::_run(bool p_current,const String& p_custom) {
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("Could not start subprocess!");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
return;
}
@@ -1483,8 +1861,10 @@ void EditorNode::_run(bool p_current,const String& p_custom) {
emit_signal("play_pressed");
if (p_current) {
play_scene_button->set_pressed(true);
+ play_scene_button->set_icon(gui_base->get_icon("Reload","EditorIcons"));
} else {
play_button->set_pressed(true);
+ play_button->set_icon(gui_base->get_icon("Reload","EditorIcons"));
}
_playing_edited=p_current;
@@ -1493,8 +1873,8 @@ void EditorNode::_run(bool p_current,const String& p_custom) {
void EditorNode::_cleanup_scene() {
-
- Node *scene = edited_scene;
+#if 0
+ Node *scene = editor_data.get_edited_scene_root();
editor_selection->clear();
editor_data.clear_editor_states();
editor_history.clear();
@@ -1503,7 +1883,7 @@ void EditorNode::_cleanup_scene() {
property_editor->edit(NULL);
resources_dock->cleanup();
scene_import_metadata.unref();
- set_edited_scene(NULL);
+ //set_edited_scene(NULL);
if (scene) {
if (scene->get_filename()!="") {
previous_scenes.push_back(scene->get_filename());
@@ -1529,35 +1909,43 @@ void EditorNode::_cleanup_scene() {
}
_update_title();
-
+#endif
}
void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
- current_option=(MenuOptions)p_option;
+ //print_line("option "+itos(p_option)+" confirm "+itos(p_confirmed));
+ if (!p_confirmed) //this may be a hack..
+ current_option=(MenuOptions)p_option;
switch( p_option ) {
case FILE_NEW_SCENE: {
+ /*
if (!p_confirmed) {
confirmation->get_ok()->set_text("Yes");
//confirmation->get_cancel()->show();
confirmation->set_text("Start a New Scene? (Current will be lost)");
- confirmation->popup_centered(Size2(300,70));
+ confirmation->popup_centered_minsize();
break;
- }
+ }*/
+
+ int idx = editor_data.add_edited_scene(-1);
+ _scene_tab_changed(idx);
+ editor_data.clear_editor_states();
- _cleanup_scene();
+ //_cleanup_scene();
} break;
+ case FILE_NEW_INHERITED_SCENE:
case FILE_OPEN_SCENE: {
//print_tree();
- file->set_mode(FileDialog::MODE_OPEN_FILE);
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
//not for now?
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions);
@@ -1569,11 +1957,11 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
//file->set_current_path(current_path);
- Node *scene = edited_scene;
+ Node *scene = editor_data.get_edited_scene_root();
if (scene) {
file->set_current_path(scene->get_filename());
};
- file->set_title("Open Scene");
+ file->set_title(p_option==FILE_OPEN_SCENE?"Open Scene":"Open Base Scene");
file->popup_centered_ratio();
} break;
@@ -1590,6 +1978,13 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
quick_open->set_title("Quick Open Script..");
} break;
+ case FILE_QUICK_OPEN_FILE: {
+
+
+ quick_open->popup("Resource",false,true);
+ quick_open->set_title("Quick Search File..");
+
+ } break;
case FILE_RUN_SCRIPT: {
file_script->popup_centered_ratio();
@@ -1602,20 +1997,41 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
open_request(previous_scenes.back()->get());
} break;
+ case FILE_CLOSE: {
+
+ if (!p_confirmed) {
+ confirmation->get_ok()->set_text("Yes");
+ //confirmation->get_cancel()->show();
+ confirmation->set_text("Close scene? (Unsaved changes will be lost)");
+ confirmation->popup_centered_minsize();
+ break;
+ }
+
+ _remove_edited_scene();
+
+
+
+ } break;
+ case SCENE_TAB_CLOSE: {
+ _remove_scene(tab_closing);
+ _update_scene_tabs();
+ current_option = -1;
+ } break;
case FILE_SAVE_SCENE: {
- Node *scene = edited_scene;
+ Node *scene = editor_data.get_edited_scene_root();
if (scene && scene->get_filename()!="") {
- _save_scene(scene->get_filename());
+ //_save_scene(scene->get_filename());
+ _save_scene_with_preview(scene->get_filename());
return;
};
// fallthrough to save_as
- };
+ } break;
case FILE_SAVE_AS_SCENE: {
- Node *scene = edited_scene;
+ Node *scene = editor_data.get_edited_scene_root();
if (!scene) {
@@ -1623,11 +2039,11 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("This operation can't be done without a tree root.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
- file->set_mode(FileDialog::MODE_SAVE_FILE);
+ file->set_mode(EditorFileDialog::MODE_SAVE_FILE);
bool relpaths = (scene->has_meta("__editor_relpaths__") && scene->get_meta("__editor_relpaths__").operator bool());
@@ -1663,9 +2079,21 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
} break;
+ case FILE_SAVE_BEFORE_RUN: {
+ if (!p_confirmed) {
+ accept->get_ok()->set_text("Yes");
+ accept->set_text("This scene has never been saved. Save before running?");
+ accept->popup_centered_minsize();
+ break;
+ }
+
+ _menu_option(FILE_SAVE_AS_SCENE);
+ _menu_option_confirm(FILE_SAVE_AND_RUN, true);
+ } break;
+
case FILE_DUMP_STRINGS: {
- Node *scene = edited_scene;
+ Node *scene = editor_data.get_edited_scene_root();
if (!scene) {
@@ -1673,7 +2101,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("This operation can't be done without a tree root.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
@@ -1691,14 +2119,14 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("Please save the scene first.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
bool relpaths = (scene->has_meta("__editor_relpaths__") && scene->get_meta("__editor_relpaths__").operator bool());
- file->set_mode(FileDialog::MODE_SAVE_FILE);
+ file->set_mode(EditorFileDialog::MODE_SAVE_FILE);
file->set_current_path(cpath);
file->set_title("Save Translatable Strings");
@@ -1709,7 +2137,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
case FILE_SAVE_SUBSCENE: {
- Node *scene = edited_scene;
+ Node *scene = editor_data.get_edited_scene_root();
if (!scene) {
@@ -1717,7 +2145,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("This operation can't be done without a scene.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
@@ -1730,24 +2158,24 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("This operation requieres a single selected node.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
Node *tocopy = selection.front()->get();
- if (tocopy!=edited_scene && tocopy->get_filename()!="") {
+ if (tocopy!=editor_data.get_edited_scene_root() && tocopy->get_filename()!="") {
current_option=-1;
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("This operation can't be done on instanced scenes.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
- file->set_mode(FileDialog::MODE_SAVE_FILE);
+ file->set_mode(EditorFileDialog::MODE_SAVE_FILE);
List<String> extensions;
Ref<PackedScene> sd = memnew( PackedScene );
@@ -1770,7 +2198,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
file->set_title("Save Sub-Scene As..");
} break;
case FILE_SAVE_OPTIMIZED: {
- Node *scene = edited_scene;
+ Node *scene = editor_data.get_edited_scene_root();
#if 0
if (!scene) {
@@ -1832,13 +2260,13 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
case FILE_EXPORT_MESH_LIBRARY: {
- if (!edited_scene) {
+ if (!editor_data.get_edited_scene_root()) {
current_option=-1;
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("This operation can't be done without a scene.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
@@ -1877,13 +2305,13 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
//import_subscene->popup_centered_ratio();
- if (!edited_scene) {
+ if (!editor_data.get_edited_scene_root()) {
current_option=-1;
//accept->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("This operation can't be done without a selected node.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
@@ -1893,14 +2321,17 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
case FILE_QUIT: {
+
if (!p_confirmed) {
+
confirmation->get_ok()->set_text("Quit");
//confirmation->get_cancel()->show();
confirmation->set_text("Exit the Editor?");
- confirmation->popup_centered(Size2(300,70));
+ confirmation->popup_centered(Size2(180,70));
break;
}
+
_menu_option_confirm(RUN_STOP,true);
get_tree()->quit();
@@ -1912,7 +2343,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
confirmation->get_ok()->set_text("Open");
//confirmation->get_cancel()->show();
confirmation->set_text("Current scene not saved. Open anyway?");
- confirmation->popup_centered(Size2(300,70));
+ confirmation->popup_centered_minsize();
break;
}
@@ -1948,6 +2379,25 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
log->add_message("REDO: "+action);
} break;
+
+ case EDIT_REVERT: {
+
+ Node *scene = get_edited_scene();
+
+ if (!scene)
+ break;
+
+ if (unsaved_cache && !p_confirmed) {
+ confirmation->get_ok()->set_text("Revert");
+ confirmation->set_text("This action cannot be undone. Revert anyway?");
+ confirmation->popup_centered_minsize();
+ break;
+ }
+
+ Error err = load_scene(scene->get_filename());
+
+ } break;
+
#if 0
case NODE_EXTERNAL_INSTANCE: {
@@ -2002,7 +2452,70 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
} break;
#endif
-
+ case RESOURCE_NEW: {
+
+ create_dialog->popup_centered_ratio();
+ } break;
+ case RESOURCE_LOAD: {
+
+ open_resource();
+ } break;
+ case RESOURCE_SAVE: {
+
+
+ uint32_t current = editor_history.get_current();
+ Object *current_obj = current>0 ? ObjectDB::get_instance(current) : NULL;
+
+ ERR_FAIL_COND(!current_obj->cast_to<Resource>())
+
+ RES current_res = RES(current_obj->cast_to<Resource>());
+
+ save_resource(current_res);
+
+ } break;
+ case RESOURCE_SAVE_AS: {
+
+ uint32_t current = editor_history.get_current();
+ Object *current_obj = current>0 ? ObjectDB::get_instance(current) : NULL;
+
+ ERR_FAIL_COND(!current_obj->cast_to<Resource>())
+
+ RES current_res = RES(current_obj->cast_to<Resource>());
+
+ save_resource_as(current_res);
+
+ } break;
+ case RESOURCE_UNREF: {
+
+ uint32_t current = editor_history.get_current();
+ Object *current_obj = current>0 ? ObjectDB::get_instance(current) : NULL;
+
+ ERR_FAIL_COND(!current_obj->cast_to<Resource>())
+
+ RES current_res = RES(current_obj->cast_to<Resource>());
+ current_res->set_path("");
+ _edit_current();
+ } break;
+ case RESOURCE_COPY: {
+
+ uint32_t current = editor_history.get_current();
+ Object *current_obj = current>0 ? ObjectDB::get_instance(current) : NULL;
+
+ ERR_FAIL_COND(!current_obj->cast_to<Resource>())
+
+ RES current_res = RES(current_obj->cast_to<Resource>());
+
+ EditorSettings::get_singleton()->set_resource_clipboard(current_res);
+
+ } break;
+ case RESOURCE_PASTE: {
+
+ RES r = EditorSettings::get_singleton()->get_resource_clipboard();
+ if (r.is_valid()) {
+ push_item(EditorSettings::get_singleton()->get_resource_clipboard().ptr(),String());
+ }
+
+ } break;
case OBJECT_REQUEST_HELP: {
if (current) {
@@ -2060,7 +2573,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
}
editor_data.get_undo_redo().clear_history();
- if (editor_plugin_screen) { //reload editor plugin
+ if (editor_plugin_over) { //reload editor plugin
editor_plugin_over->edit(NULL);
editor_plugin_over->edit(current);
}
@@ -2073,12 +2586,12 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
call_dialog->popup_centered_ratio();
} break;
case RUN_PLAY: {
-
+ _menu_option_confirm(RUN_STOP,true);
_run(false);
} break;
case RUN_PLAY_CUSTOM_SCENE: {
-
+ _menu_option_confirm(RUN_STOP,true);
quick_run->popup("PackedScene",true);
quick_run->set_title("Quick Run Scene..");
@@ -2095,16 +2608,25 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
editor_run.stop();
play_button->set_pressed(false);
+ play_button->set_icon(gui_base->get_icon("Play","EditorIcons"));
play_scene_button->set_pressed(false);
- pause_button->set_pressed(false);
+ play_scene_button->set_icon(gui_base->get_icon("PlayScene","EditorIcons"));
+ //pause_button->set_pressed(false);
emit_signal("stop_pressed");
} break;
case RUN_PLAY_SCENE: {
-
+ _menu_option_confirm(RUN_STOP,true);
_run(true);
} break;
+ case RUN_PLAY_NATIVE: {
+ _menu_option_confirm(RUN_STOP,true);
+ emit_signal("play_pressed");
+ editor_run.run_native_notify();
+
+
+ } break;
case RUN_SCENE_SETTINGS: {
run_settings_dialog->popup_run_settings();
@@ -2118,7 +2640,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
if (!p_confirmed) {
confirmation->get_ok()->set_text("Yes");
confirmation->set_text("Open Project Manager? \n(Unsaved changes will be lost)");
- confirmation->popup_centered(Size2(300,70));
+ confirmation->popup_centered_minsize();
break;
}
@@ -2136,28 +2658,57 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
case RUN_FILE_SERVER: {
//file_server
- bool ischecked = fileserver_menu->get_popup()->is_item_checked( fileserver_menu->get_popup()->get_item_index(RUN_FILE_SERVER));
+ bool ischecked = debug_button->get_popup()->is_item_checked( debug_button->get_popup()->get_item_index(RUN_FILE_SERVER));
if (ischecked) {
file_server->stop();
- fileserver_menu->set_icon(gui_base->get_icon("FileServer","EditorIcons"));
- fileserver_menu->get_popup()->set_item_text( fileserver_menu->get_popup()->get_item_index(RUN_FILE_SERVER),"Enable File Server");
+ //debug_button->set_icon(gui_base->get_icon("FileServer","EditorIcons"));
+ //debug_button->get_popup()->set_item_text( debug_button->get_popup()->get_item_index(RUN_FILE_SERVER),"Enable File Server");
} else {
file_server->start();
- fileserver_menu->set_icon(gui_base->get_icon("FileServerActive","EditorIcons"));
- fileserver_menu->get_popup()->set_item_text( fileserver_menu->get_popup()->get_item_index(RUN_FILE_SERVER),"Disable File Server");
+ //debug_button->set_icon(gui_base->get_icon("FileServerActive","EditorIcons"));
+ //debug_button->get_popup()->set_item_text( debug_button->get_popup()->get_item_index(RUN_FILE_SERVER),"Disable File Server");
}
- fileserver_menu->get_popup()->set_item_checked( fileserver_menu->get_popup()->get_item_index(RUN_FILE_SERVER),!ischecked);
+ debug_button->get_popup()->set_item_checked( debug_button->get_popup()->get_item_index(RUN_FILE_SERVER),!ischecked);
+
+ } break;
+ case RUN_LIVE_DEBUG: {
+
+ bool ischecked = debug_button->get_popup()->is_item_checked( debug_button->get_popup()->get_item_index(RUN_LIVE_DEBUG));
+ debug_button->get_popup()->set_item_checked( debug_button->get_popup()->get_item_index(RUN_LIVE_DEBUG),!ischecked);
+ ScriptEditor::get_singleton()->get_debugger()->set_live_debugging(!ischecked);
} break;
+
case RUN_DEPLOY_DUMB_CLIENTS: {
- bool ischecked = fileserver_menu->get_popup()->is_item_checked( fileserver_menu->get_popup()->get_item_index(RUN_DEPLOY_DUMB_CLIENTS));
- fileserver_menu->get_popup()->set_item_checked( fileserver_menu->get_popup()->get_item_index(RUN_DEPLOY_DUMB_CLIENTS),!ischecked);
+ bool ischecked = debug_button->get_popup()->is_item_checked( debug_button->get_popup()->get_item_index(RUN_DEPLOY_DUMB_CLIENTS));
+ debug_button->get_popup()->set_item_checked( debug_button->get_popup()->get_item_index(RUN_DEPLOY_DUMB_CLIENTS),!ischecked);
run_native->set_deploy_dumb(!ischecked);
} break;
+ case RUN_DEPLOY_REMOTE_DEBUG: {
+
+ bool ischecked = debug_button->get_popup()->is_item_checked( debug_button->get_popup()->get_item_index(RUN_DEPLOY_REMOTE_DEBUG));
+ debug_button->get_popup()->set_item_checked( debug_button->get_popup()->get_item_index(RUN_DEPLOY_REMOTE_DEBUG),!ischecked);
+ run_native->set_deploy_debug_remote(!ischecked);
+
+ } break;
+ case RUN_DEBUG_COLLISONS: {
+
+ bool ischecked = debug_button->get_popup()->is_item_checked( debug_button->get_popup()->get_item_index(RUN_DEBUG_COLLISONS));
+ debug_button->get_popup()->set_item_checked( debug_button->get_popup()->get_item_index(RUN_DEBUG_COLLISONS),!ischecked);
+ run_native->set_debug_collisions(!ischecked);
+ editor_run.set_debug_collisions(!ischecked);
+ } break;
+ case RUN_DEBUG_NAVIGATION: {
+
+ bool ischecked = debug_button->get_popup()->is_item_checked( debug_button->get_popup()->get_item_index(RUN_DEBUG_NAVIGATION));
+ debug_button->get_popup()->set_item_checked( debug_button->get_popup()->get_item_index(RUN_DEBUG_NAVIGATION),!ischecked);
+ run_native->set_debug_navigation(!ischecked);
+ editor_run.set_debug_navigation(!ischecked);
+ } break;
case SETTINGS_UPDATE_ALWAYS: {
update_menu->get_popup()->set_item_checked(0,true);
@@ -2306,7 +2857,7 @@ Control* EditorNode::get_viewport() {
void EditorNode::_editor_select(int p_which) {
static bool selecting=false;
- if (selecting)
+ if (selecting || changing_scene)
return;
selecting=true;
@@ -2314,7 +2865,9 @@ void EditorNode::_editor_select(int p_which) {
ERR_FAIL_INDEX(p_which,editor_table.size());
- main_editor_tabs->set_current_tab(p_which);
+ for(int i=0;i<main_editor_buttons.size();i++) {
+ main_editor_buttons[i]->set_pressed(i==p_which);
+ }
selecting=false;
@@ -2332,6 +2885,8 @@ void EditorNode::_editor_select(int p_which) {
editor_plugin_screen=new_editor;
editor_plugin_screen->make_visible(true);
editor_plugin_screen->selected_notify();
+
+
}
void EditorNode::add_editor_plugin(EditorPlugin *p_editor) {
@@ -2339,7 +2894,12 @@ void EditorNode::add_editor_plugin(EditorPlugin *p_editor) {
if (p_editor->has_main_screen()) {
- singleton->main_editor_tabs->add_tab(p_editor->get_name());
+ ToolButton *tb = memnew( ToolButton );
+ tb->set_toggle_mode(true);
+ tb->connect("pressed",singleton,"_editor_select",varray(singleton->main_editor_buttons.size()));
+ tb->set_text(p_editor->get_name());
+ singleton->main_editor_buttons.push_back(tb);
+ singleton->main_editor_button_vb->add_child(tb);
singleton->editor_table.push_back(p_editor);
}
singleton->editor_data.add_editor_plugin( p_editor );
@@ -2351,16 +2911,18 @@ void EditorNode::remove_editor_plugin(EditorPlugin *p_editor) {
if (p_editor->has_main_screen()) {
- for(int i=0;i<singleton->main_editor_tabs->get_tab_count();i++) {
+ for(int i=0;i<singleton->main_editor_buttons.size();i++) {
+
+ if (p_editor->get_name()==singleton->main_editor_buttons[i]->get_name()) {
- if (p_editor->get_name()==singleton->main_editor_tabs->get_tab_title(i)) {
+ memdelete( singleton->main_editor_buttons[i] );
+ singleton->main_editor_buttons.remove(i);
- singleton->main_editor_tabs->remove_tab(i);
break;
}
}
- singleton->main_editor_tabs->add_tab(p_editor->get_name());
+ //singleton->main_editor_tabs->add_tab(p_editor->get_name());
singleton->editor_table.erase(p_editor);
}
singleton->remove_child(p_editor);
@@ -2369,29 +2931,80 @@ void EditorNode::remove_editor_plugin(EditorPlugin *p_editor) {
}
+void EditorNode::add_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import) {
+
+ editor_import_export->add_import_plugin(p_editor_import);
+ _rebuild_import_menu();
+}
+
+void EditorNode::remove_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import) {
+
+ editor_import_export->remove_import_plugin(p_editor_import);
+ _rebuild_import_menu();
+}
+
+void EditorNode::_remove_edited_scene() {
+ int new_index = editor_data.get_edited_scene();
+ int old_index=new_index;
+
+ if (new_index>0) {
+ new_index=new_index-1;
+ } else if (editor_data.get_edited_scene_count()>1) {
+ new_index=1;
+ } else {
+ editor_data.add_edited_scene(-1);
+ new_index=1;
+ }
+
+ _scene_tab_changed(new_index);
+ editor_data.remove_scene(old_index);
+ editor_data.get_undo_redo().clear_history();
+ _update_title();
+ _update_scene_tabs();
+
+// if (editor_data.get_edited_scene_count()==1) {
+// //make new scene appear saved
+// set_current_version(editor_data.get_undo_redo().get_version());
+// unsaved_cache=false;
+// }
+}
+
+void EditorNode::_remove_scene(int index) {
+// printf("Attempting to remove scene %d (current is %d)\n", index, editor_data.get_edited_scene());
+
+ if (editor_data.get_edited_scene() == index) {
+ //Scene to remove is current scene
+ _remove_edited_scene();
+ }
+ else {
+ // Scene to remove is not active scene
+ editor_data.remove_scene(index);
+ }
+}
+
void EditorNode::set_edited_scene(Node *p_scene) {
- if (edited_scene) {
- if (edited_scene->get_parent()==scene_root)
- scene_root->remove_child(edited_scene);
+ if (get_editor_data().get_edited_scene_root()) {
+ if (get_editor_data().get_edited_scene_root()->get_parent()==scene_root)
+ scene_root->remove_child(get_editor_data().get_edited_scene_root());
animation_editor->set_root(NULL);
}
- edited_scene=p_scene;
- if (edited_scene && edited_scene->cast_to<Popup>())
- edited_scene->cast_to<Popup>()->show(); //show popups
- scene_tree_dock->set_edited_scene(edited_scene);
+ get_editor_data().set_edited_scene_root(p_scene);
+
+ if (p_scene && p_scene->cast_to<Popup>())
+ p_scene->cast_to<Popup>()->show(); //show popups
+ scene_tree_dock->set_edited_scene(p_scene);
if (get_tree())
- get_tree()->set_edited_scene_root(edited_scene);
+ get_tree()->set_edited_scene_root(p_scene);
- if (edited_scene) {
+ if (p_scene) {
if (p_scene->get_parent()!=scene_root)
scene_root->add_child(p_scene);
animation_editor->set_root(p_scene);
}
-
-
}
+
void EditorNode::_fetch_translatable_strings(const Object *p_object,Set<StringName>& strings) {
@@ -2443,7 +3056,7 @@ Error EditorNode::save_translatable_strings(const String& p_to_file) {
OS::Time time = OS::get_singleton()->get_time();
f->store_line("# Translation Strings Dump.");
f->store_line("# Created By.");
- f->store_line("# \t"VERSION_FULL_NAME" (c) 2008-2014 Juan Linietsky, Ariel Manzur.");
+ f->store_line("# \t" VERSION_FULL_NAME " (c) 2008-2015 Juan Linietsky, Ariel Manzur.");
f->store_line("# From Scene: ");
f->store_line("# \t"+get_edited_scene()->get_filename());
f->store_line("");
@@ -2558,7 +3171,7 @@ Error EditorNode::save_optimized_copy(const String& p_scene,const String& p_pres
}
}
- ERR_EXPLAIN("Preset '"+p_preset+"' references unexisting saver: "+type);
+ ERR_EXPLAIN("Preset '"+p_preset+"' references nonexistent saver: "+type);
ERR_FAIL_COND_V(saver.is_null(),ERR_INVALID_DATA);
List<Variant> keys;
@@ -2617,7 +3230,179 @@ Error EditorNode::save_optimized_copy(const String& p_scene,const String& p_pres
return OK;
}
-Error EditorNode::load_scene(const String& p_scene) {
+
+int EditorNode::_get_current_main_editor() {
+
+ for(int i=0;i<editor_table.size();i++) {
+ if (editor_table[i]==editor_plugin_screen)
+ return i;
+ }
+
+ return 0;
+}
+
+Dictionary EditorNode::_get_main_scene_state() {
+
+ Dictionary state;
+ state["main_tab"]=_get_current_main_editor();
+ state["scene_tree_offset"]=scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_val();
+ state["property_edit_offset"]=get_property_editor()->get_scene_tree()->get_vscroll_bar()->get_val();
+ state["saved_version"]=saved_version;
+ //print_line(" getting main tab: "+itos(state["main_tab"]));
+ return state;
+}
+
+void EditorNode::_set_main_scene_state(Dictionary p_state) {
+
+ //print_line("set current 7 ");
+ changing_scene=false;
+
+#if 0
+ if (p_state.has("main_tab")) {
+ int idx = p_state["main_tab"];
+
+
+ print_line("comes with tab: "+itos(idx));
+ int current=-1;
+ for(int i=0;i<editor_table.size();i++) {
+ if (editor_plugin_screen==editor_table[i]) {
+ current=i;
+ break;
+ }
+ }
+
+
+ if (idx<2 && current<2) {
+ //only set tab for 2D and 3D
+ _editor_select(idx);
+ //print_line(" setting main tab: "+itos(p_state["main_tab"]));
+ }
+ }
+#else
+
+ if (get_edited_scene()) {
+
+ int current=-1;
+ for(int i=0;i<editor_table.size();i++) {
+ if (editor_plugin_screen==editor_table[i]) {
+ current=i;
+ break;
+ }
+ }
+
+ if (current<2) {
+ //use heuristic instead
+
+ int n2d=0,n3d=0;
+ _find_node_types(get_edited_scene(),n2d,n3d);
+ if (n2d>n3d) {
+ _editor_select(0);
+ } else if (n3d>n2d) {
+ _editor_select(1);
+
+ }
+ }
+
+ }
+#endif
+
+
+ if (p_state.has("scene_tree_offset"))
+ scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->set_val(p_state["scene_tree_offset"]);
+ if (p_state.has("property_edit_offset"))
+ get_property_editor()->get_scene_tree()->get_vscroll_bar()->set_val(p_state["property_edit_offset"]);
+
+ //print_line("set current 8 ");
+
+ //this should only happen at the very end
+
+ //changing_scene=true; //avoid script change from opening editor
+ ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root();
+ ScriptEditor::get_singleton()->set_scene_root_script( editor_data.get_scene_root_script(editor_data.get_edited_scene()) );
+ //changing_scene=false;
+
+}
+
+void EditorNode::set_current_version(uint64_t p_version) {
+
+ saved_version=p_version;
+ editor_data.set_edited_scene_version(p_version);
+}
+
+bool EditorNode::is_changing_scene() const {
+ return changing_scene;
+}
+void EditorNode::set_current_scene(int p_idx) {
+
+ changing_scene=true;
+ editor_data.save_edited_scene_state(editor_selection,&editor_history,_get_main_scene_state());
+
+ if (get_editor_data().get_edited_scene_root()) {
+ if (get_editor_data().get_edited_scene_root()->get_parent()==scene_root)
+ scene_root->remove_child(get_editor_data().get_edited_scene_root());
+ animation_editor->set_root(NULL);
+ }
+
+ //print_line("set current 2 ");
+
+ editor_selection->clear();
+ editor_data.set_edited_scene(p_idx);
+
+ Node* new_scene = editor_data.get_edited_scene_root();
+
+ if (new_scene && new_scene->cast_to<Popup>())
+ new_scene->cast_to<Popup>()->show(); //show popups
+
+ //print_line("set current 3 ");
+
+ scene_tree_dock->set_edited_scene(new_scene);
+ if (get_tree())
+ get_tree()->set_edited_scene_root(new_scene);
+
+ if (new_scene) {
+ if (new_scene->get_parent()!=scene_root)
+ scene_root->add_child(new_scene);
+ animation_editor->set_root(new_scene);
+ }
+ //print_line("set current 4 ");
+
+
+ Dictionary state = editor_data.restore_edited_scene_state(editor_selection,&editor_history);
+ _edit_current();
+
+ /*if (!unsaved) {
+ saved_version=editor_data.get_undo_redo().get_version();
+ if (p_backwards)
+ saved_version--;
+ else
+ saved_version++;
+ print_line("was saved, updating version");
+ } else {
+ saved_version=state["saved_version"];
+ }*/
+ //_set_main_scene_state(state);
+
+ call_deferred("_set_main_scene_state",state); //do after everything else is done setting up
+ //print_line("set current 6 ");
+
+
+}
+
+bool EditorNode::is_scene_open(const String& p_path) {
+
+ for(int i=0;i<editor_data.get_edited_scene_count();i++) {
+ if (editor_data.get_scene_path(i)==p_path)
+ return true;
+ }
+
+ return false;
+}
+
+void EditorNode::fix_dependencies(const String& p_for_file) {
+ dependency_fixer->edit(p_for_file);
+}
+
+Error EditorNode::load_scene(const String& p_scene, bool p_ignore_broken_deps,bool p_set_inherited) {
if (!is_inside_tree()) {
defer_load_scene = p_scene;
@@ -2625,6 +3410,14 @@ Error EditorNode::load_scene(const String& p_scene) {
}
+ for(int i=0;i<editor_data.get_edited_scene_count();i++) {
+
+ if (editor_data.get_scene_path(i)==p_scene) {
+ _scene_tab_changed(i);
+ return OK;
+ }
+ }
+
load_errors->clear();
String lpath = Globals::get_singleton()->localize_path(p_scene);
@@ -2635,38 +3428,97 @@ Error EditorNode::load_scene(const String& p_scene) {
//accept->get_cancel()->hide();
accept->get_ok()->set_text("Ugh");
accept->set_text("Error loading scene, it must be inside the project path. Use 'Import' to open the scene, then save it inside the project path.");
- accept->popup_centered(Size2(300,120));
+ accept->popup_centered_minsize();
opening_prev=false;
return ERR_FILE_NOT_FOUND;
}
- _cleanup_scene(); // i'm sorry but this MUST happen to avoid modified resources to not be reloaded.
+ int prev = editor_data.get_edited_scene();
+ int idx = editor_data.add_edited_scene(-1);
+ //print_line("load scene callback");
+ //set_current_scene(idx);
- Ref<PackedScene> sdata = ResourceLoader::load(lpath);
+ if (!editor_data.get_edited_scene_root() && editor_data.get_edited_scene_count()==2) {
+ _remove_edited_scene();
+ } else {
+ _scene_tab_changed(idx);
+ }
+
+
+
+ //_cleanup_scene(); // i'm sorry but this MUST happen to avoid modified resources to not be reloaded.
+
+ dependency_errors.clear();
+
+ Ref<PackedScene> sdata = ResourceLoader::load(lpath,"",true);
if (!sdata.is_valid()) {
current_option=-1;
//accept->get_cancel()->hide();
accept->get_ok()->set_text("Ugh");
accept->set_text("Error loading scene.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
opening_prev=false;
+
+ if (prev!=-1) {
+ set_current_scene(prev);
+ editor_data.remove_scene(idx);
+ }
return ERR_FILE_NOT_FOUND;
}
+ if (!p_ignore_broken_deps && dependency_errors.has(lpath)) {
+
+ current_option=-1;
+ Vector<String> errors;
+ for(Set<String>::Element *E=dependency_errors[lpath].front();E;E=E->next()) {
+
+ errors.push_back(E->get());
+ }
+ dependency_error->show(lpath,errors);
+ opening_prev=false;
+
+ if (prev!=-1) {
+ set_current_scene(prev);
+ editor_data.remove_scene(idx);
+ }
+ return ERR_FILE_MISSING_DEPENDENCIES;
+ }
+
+ dependency_errors.erase(lpath); //at least not self path
+
+ for (Map<String,Set<String> >::Element *E=dependency_errors.front();E;E=E->next()) {
+
+ String txt="Scene '"+E->key()+"' has broken dependencies:\n";
+ for(Set<String>::Element *F=E->get().front();F;F=F->next()) {
+ txt+="\t"+F->get()+"\n";
+ }
+ add_io_error(txt);
+ }
+
+ sdata->set_path(lpath,true); //take over path
+
Node*new_scene=sdata->instance(true);
if (!new_scene) {
+ sdata.unref();
current_option=-1;
//accept->get_cancel()->hide();
accept->get_ok()->set_text("Ugh");
accept->set_text("Error loading scene.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
opening_prev=false;
+ if (prev!=-1) {
+ set_current_scene(prev);
+ editor_data.remove_scene(idx);
+ }
return ERR_FILE_NOT_FOUND;
}
+ //guess not needed in the end?
+ //new_scene->clear_internal_tree_resource_paths(); //make sure no internal tree paths to internal resources exist
+
/*
Node *old_scene = edited_scene;
_hide_top_editors();
@@ -2679,16 +3531,33 @@ Error EditorNode::load_scene(const String& p_scene) {
memdelete(old_scene);
}
*/
+
+ if (p_set_inherited) {
+ Ref<SceneState> state = sdata->get_state();
+ state->set_path(lpath);
+ new_scene->set_scene_inherited_state(state);
+ new_scene->set_filename(String());
+ if (new_scene->get_scene_instance_state().is_valid())
+ new_scene->get_scene_instance_state()->set_path(String());
+ }
+
+
set_edited_scene(new_scene);
_get_scene_metadata();
+ /*
+ editor_data.set_edited_scene_root(new_scene);
+
scene_tree_dock->set_selected(new_scene, true);
property_editor->edit(new_scene);
- scene_import_metadata = sdata->get_import_metadata();
+ editor_data.set_edited_scene_root(new_scene);
+*/
+ editor_data.set_edited_scene_import_metadata( sdata->get_import_metadata() );
- editor_data.get_undo_redo().clear_history();
+
+// editor_data.get_undo_redo().clear_history();
saved_version=editor_data.get_undo_redo().get_version();
_update_title();
-
+ _update_scene_tabs();
_add_to_recent_scenes(lpath);
if (new_scene->has_meta("__editor_plugin_screen__")) {
@@ -2707,7 +3576,9 @@ Error EditorNode::load_scene(const String& p_scene) {
prev_scene->set_disabled(previous_scenes.size()==0);
opening_prev=false;
- top_pallete->set_current_tab(0); //always go to scene
+ ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root();
+
+ //top_pallete->set_current_tab(0); //always go to scene
push_item(new_scene);
@@ -2718,8 +3589,9 @@ Error EditorNode::load_scene(const String& p_scene) {
void EditorNode::open_request(const String& p_path) {
- external_file=p_path;
- _menu_option_confirm(FILE_EXTERNAL_OPEN_SCENE,false);
+ load_scene(p_path); // as it will be opened in separate tab
+ //external_file=p_path;
+ //_menu_option_confirm(FILE_EXTERNAL_OPEN_SCENE,false);
}
@@ -2740,9 +3612,9 @@ void EditorNode::_instance_request(const String& p_path){
request_instance_scene(p_path);
}
-void EditorNode::_property_keyed(const String& p_keyed,const Variant& p_value) {
+void EditorNode::_property_keyed(const String& p_keyed,const Variant& p_value,bool p_advance) {
- animation_editor->insert_value_key(p_keyed,p_value);
+ animation_editor->insert_value_key(p_keyed,p_value,p_advance);
}
void EditorNode::_transform_keyed(Object *sp,const String& p_sub,const Transform& p_key) {
@@ -2780,15 +3652,15 @@ void EditorNode::_update_keying() {
void EditorNode::_close_messages() {
// left_split->set_dragger_visible(false);
- old_split_ofs = left_split->get_split_offset();
- left_split->set_split_offset(0);
+ old_split_ofs = center_split->get_split_offset();
+ center_split->set_split_offset(0);
// scene_root_parent->set_anchor_and_margin(MARGIN_BOTTOM,Control::ANCHOR_END,0);
}
void EditorNode::_show_messages() {
// left_split->set_dragger_visible(true);
- left_split->set_split_offset(old_split_ofs);
+ center_split->set_split_offset(old_split_ofs);
// scene_root_parent->set_anchor_and_margin(MARGIN_BOTTOM,Control::ANCHOR_END,log->get_margin(MARGIN_TOP));
}
@@ -2857,15 +3729,16 @@ void EditorNode::_open_recent_scene(int p_idx) {
String path = "res://"+rc[p_idx];
- if (unsaved_cache) {
+
+ /*if (unsaved_cache) {
_recent_scene=rc[p_idx];
open_recent_confirmation->set_text("Discard current scene and open:\n'"+rc[p_idx]+"'");
open_recent_confirmation->get_label()->set_align(Label::ALIGN_CENTER);
open_recent_confirmation->popup_centered(Size2(400,100));
return;
- }
+ }*/
- load_scene(rc[p_idx]);
+ load_scene(path);
}
@@ -2909,12 +3782,6 @@ void EditorNode::_save_optimized() {
#endif
}
-void EditorNode::_open_recent_scene_confirm() {
-
- load_scene(_recent_scene);
-
-}
-
void EditorNode::_update_recent_scenes() {
String base="_"+Globals::get_singleton()->get_resource_path().replace("\\","::").replace("/","::");
@@ -2934,9 +3801,12 @@ void EditorNode::hide_animation_player_editors() {
void EditorNode::_quick_opened(const String& p_resource) {
- print_line("quick_opened");
- if (quick_open->get_base_type()=="PackedScene") {
+ if (current_option==FILE_QUICK_OPEN_FILE) {
+ scenes_dock->open(p_resource);
+ return;
+ }
+ if (quick_open->get_base_type()=="PackedScene") {
open_request(p_resource);
} else {
load_resource(p_resource);
@@ -2952,9 +3822,7 @@ void EditorNode::_quick_run(const String& p_resource) {
void EditorNode::notify_child_process_exited() {
- play_button->set_pressed(false);
- play_scene_button->set_pressed(false);
- pause_button->set_pressed(false);
+ _menu_option_confirm(RUN_STOP,false);
stop_button->set_pressed(false);
editor_run.stop();
@@ -3034,6 +3902,8 @@ void EditorNode::register_editor_types() {
ObjectTypeDB::register_type<EditorImportPlugin>();
ObjectTypeDB::register_type<EditorScenePostImport>();
ObjectTypeDB::register_type<EditorScript>();
+ ObjectTypeDB::register_type<EditorFileDialog>();
+ ObjectTypeDB::register_type<UndoRedo>();
//ObjectTypeDB::register_type<EditorImporter>();
@@ -3119,11 +3989,13 @@ void EditorNode::_bind_methods() {
ObjectTypeDB::bind_method("_quick_opened",&EditorNode::_quick_opened);
ObjectTypeDB::bind_method("_quick_run",&EditorNode::_quick_run);
+ ObjectTypeDB::bind_method("_resource_created",&EditorNode::_resource_created);
+
ObjectTypeDB::bind_method("_import_action",&EditorNode::_import_action);
//ObjectTypeDB::bind_method("_import",&EditorNode::_import);
// ObjectTypeDB::bind_method("_import_conflicts_solved",&EditorNode::_import_conflicts_solved);
ObjectTypeDB::bind_method("_open_recent_scene",&EditorNode::_open_recent_scene);
- ObjectTypeDB::bind_method("_open_recent_scene_confirm",&EditorNode::_open_recent_scene_confirm);
+// ObjectTypeDB::bind_method("_open_recent_scene_confirm",&EditorNode::_open_recent_scene_confirm);
ObjectTypeDB::bind_method("_save_optimized",&EditorNode::_save_optimized);
ObjectTypeDB::bind_method(_MD("animation_panel_make_visible","enable"),&EditorNode::animation_panel_make_visible);
@@ -3132,7 +4004,32 @@ void EditorNode::_bind_methods() {
ObjectTypeDB::bind_method("_sources_changed",&EditorNode::_sources_changed);
ObjectTypeDB::bind_method("_fs_changed",&EditorNode::_fs_changed);
-
+ ObjectTypeDB::bind_method("_dock_select_draw",&EditorNode::_dock_select_draw);
+ ObjectTypeDB::bind_method("_dock_select_input",&EditorNode::_dock_select_input);
+ ObjectTypeDB::bind_method("_dock_pre_popup",&EditorNode::_dock_pre_popup);
+ ObjectTypeDB::bind_method("_dock_split_dragged",&EditorNode::_dock_split_dragged);
+ ObjectTypeDB::bind_method("_save_docks",&EditorNode::_save_docks);
+ ObjectTypeDB::bind_method("_dock_popup_exit",&EditorNode::_dock_popup_exit);
+ ObjectTypeDB::bind_method("_dock_move_left",&EditorNode::_dock_move_left);
+ ObjectTypeDB::bind_method("_dock_move_right",&EditorNode::_dock_move_right);
+
+ ObjectTypeDB::bind_method("set_current_scene",&EditorNode::set_current_scene);
+ ObjectTypeDB::bind_method("set_current_version",&EditorNode::set_current_version);
+ ObjectTypeDB::bind_method("_scene_tab_changed",&EditorNode::_scene_tab_changed);
+ ObjectTypeDB::bind_method("_scene_tab_closed",&EditorNode::_scene_tab_closed);
+ ObjectTypeDB::bind_method("_scene_tab_script_edited",&EditorNode::_scene_tab_script_edited);
+ ObjectTypeDB::bind_method("_set_main_scene_state",&EditorNode::_set_main_scene_state);
+ ObjectTypeDB::bind_method("_update_scene_tabs",&EditorNode::_update_scene_tabs);
+
+ ObjectTypeDB::bind_method("_prepare_history",&EditorNode::_prepare_history);
+ ObjectTypeDB::bind_method("_select_history",&EditorNode::_select_history);
+
+ ObjectTypeDB::bind_method("_toggle_search_bar",&EditorNode::_toggle_search_bar);
+ ObjectTypeDB::bind_method("_clear_search_box",&EditorNode::_clear_search_box);
+
+ ObjectTypeDB::bind_method(_MD("add_editor_import_plugin", "plugin"), &EditorNode::add_editor_import_plugin);
+ ObjectTypeDB::bind_method(_MD("remove_editor_import_plugin", "plugin"), &EditorNode::remove_editor_import_plugin);
+ ObjectTypeDB::bind_method(_MD("get_gui_base"), &EditorNode::get_gui_base);
ADD_SIGNAL( MethodInfo("play_pressed") );
ADD_SIGNAL( MethodInfo("pause_pressed") );
@@ -3181,6 +4078,16 @@ void EditorNode::_file_dialog_unregister(FileDialog *p_dialog){
singleton->file_dialogs.erase(p_dialog);
}
+void EditorNode::_editor_file_dialog_register(EditorFileDialog *p_dialog) {
+
+ singleton->editor_file_dialogs.insert(p_dialog);
+}
+
+void EditorNode::_editor_file_dialog_unregister(EditorFileDialog *p_dialog){
+
+ singleton->editor_file_dialogs.erase(p_dialog);
+}
+
Vector<EditorNodeInitCallback> EditorNode::_init_callbacks;
Error EditorNode::export_platform(const String& p_platform, const String& p_path, bool p_debug,const String& p_password,bool p_quit_after) {
@@ -3193,13 +4100,469 @@ Error EditorNode::export_platform(const String& p_platform, const String& p_path
return OK;
}
+void EditorNode::show_warning(const String& p_text) {
+
+ warning->set_text(p_text);
+ warning->popup_centered_minsize();
+}
+
+void EditorNode::_dock_select_input(const InputEvent& p_input) {
+
+ if (p_input.type==InputEvent::MOUSE_BUTTON || p_input.type==InputEvent::MOUSE_MOTION) {
+
+ Vector2 point(p_input.mouse_motion.x,p_input.mouse_motion.y);
+
+ int nrect = -1;
+ for(int i=0;i<DOCK_SLOT_MAX;i++) {
+ if (dock_select_rect[i].has_point(point)) {
+ nrect=i;
+ break;
+ }
+ }
+
+
+ if (nrect!=dock_select_rect_over) {
+ dock_select->update();
+ dock_select_rect_over=nrect;
+
+ }
+
+
+ if (nrect==-1)
+ return;
+
+ if (p_input.type==InputEvent::MOUSE_BUTTON && p_input.mouse_button.button_index==1 && p_input.mouse_button.pressed && dock_popup_selected!=nrect) {
+ Control *dock = dock_slot[dock_popup_selected]->get_current_tab_control();
+ if (dock) {
+ dock_slot[dock_popup_selected]->remove_child(dock);
+ }
+ if (dock_slot[dock_popup_selected]->get_tab_count()==0) {
+ dock_slot[dock_popup_selected]->hide();
+
+ } else {
+
+ dock_slot[dock_popup_selected]->set_current_tab(0);
+ }
+
+ print_line("performing reparent");
+ dock_slot[nrect]->add_child(dock);
+ dock_popup_selected=nrect;
+ dock_slot[nrect]->set_current_tab(dock_slot[nrect]->get_tab_count()-1);
+ dock_slot[nrect]->show();
+ dock_select->update();
+
+ VSplitContainer*splits[DOCK_SLOT_MAX/2]={
+ left_l_vsplit,
+ left_r_vsplit,
+ right_l_vsplit,
+ right_r_vsplit,
+ };
+
+ for(int i=0;i<4;i++) {
+ bool in_use = dock_slot[i*2+0]->get_tab_count() || dock_slot[i*2+1]->get_tab_count();
+ if (in_use)
+ splits[i]->show();
+ else
+ splits[i]->hide();
+ }
+
+ _save_docks();
+ }
+ }
+}
+
+void EditorNode::_dock_popup_exit() {
+
+ dock_select_rect_over=-1;
+ dock_select->update();
+}
+
+void EditorNode::_dock_pre_popup(int p_which) {
+
+
+ dock_popup_selected=p_which;
+}
+
+void EditorNode::_dock_move_left() {
+
+ if (dock_popup_selected<0 || dock_popup_selected>=DOCK_SLOT_MAX)
+ return;
+ Control *current = dock_slot[dock_popup_selected]->get_tab_control( dock_slot[dock_popup_selected]->get_current_tab() );
+ Control *prev = dock_slot[dock_popup_selected]->get_tab_control( dock_slot[dock_popup_selected]->get_current_tab()-1 );
+ if (!current || !prev)
+ return;
+ dock_slot[dock_popup_selected]->move_child(current,prev->get_index());
+ dock_slot[dock_popup_selected]->set_current_tab( dock_slot[dock_popup_selected]->get_current_tab()-1 );
+ dock_select->update();
+ _save_docks();
+
+
+}
+
+void EditorNode::_dock_move_right() {
+
+ Control *current = dock_slot[dock_popup_selected]->get_tab_control( dock_slot[dock_popup_selected]->get_current_tab() );
+ Control *next = dock_slot[dock_popup_selected]->get_tab_control( dock_slot[dock_popup_selected]->get_current_tab()+1 );
+ if (!current || !next)
+ return;
+ dock_slot[dock_popup_selected]->move_child(next,current->get_index());
+ dock_slot[dock_popup_selected]->set_current_tab( dock_slot[dock_popup_selected]->get_current_tab()+1 );
+ dock_select->update();
+ _save_docks();
+}
+
+void EditorNode::_dock_select_draw(){
+ Size2 s = dock_select->get_size();
+ s.y/=2.0;
+ s.x/=6.0;
+
+ Color used=Color(0.6,0.6,0.6,0.8);
+ Color used_selected=Color(0.8,0.8,0.8,0.8);
+ Color tab_selected=Color(1,1,1,1);
+ Color unused=used;
+ unused.a=0.4;
+ Color unusable=unused;
+ unusable.a=0.1;
+
+ Rect2 unr(s.x*2,0,s.x*2,s.y*2);
+ unr.pos+=Vector2(2,5);
+ unr.size-=Vector2(4,7);
+
+ dock_select->draw_rect(unr,unusable);
+
+ dock_tab_move_left->set_disabled(true);
+ dock_tab_move_right->set_disabled(true);
+
+ if (dock_popup_selected!=-1 && dock_slot[dock_popup_selected]->get_tab_count()) {
+
+ dock_tab_move_left->set_disabled(dock_slot[dock_popup_selected]->get_current_tab()==0);
+ dock_tab_move_right->set_disabled(dock_slot[dock_popup_selected]->get_current_tab()>=dock_slot[dock_popup_selected]->get_tab_count()-1);
+ }
+
+ for(int i=0;i<DOCK_SLOT_MAX;i++) {
+
+ Vector2 ofs;
+
+ switch(i) {
+ case DOCK_SLOT_LEFT_UL: {
+
+ } break;
+ case DOCK_SLOT_LEFT_BL: {
+ ofs.y+=s.y;
+ } break;
+ case DOCK_SLOT_LEFT_UR: {
+ ofs.x+=s.x;
+ } break;
+ case DOCK_SLOT_LEFT_BR: {
+ ofs+=s;
+ } break;
+ case DOCK_SLOT_RIGHT_UL: {
+ ofs.x+=s.x*4;
+ } break;
+ case DOCK_SLOT_RIGHT_BL: {
+ ofs.x+=s.x*4;
+ ofs.y+=s.y;
+
+ } break;
+ case DOCK_SLOT_RIGHT_UR: {
+ ofs.x+=s.x*4;
+ ofs.x+=s.x;
+
+ } break;
+ case DOCK_SLOT_RIGHT_BR: {
+ ofs.x+=s.x*4;
+ ofs+=s;
+
+ } break;
+ }
+
+ Rect2 r(ofs,s);
+ dock_select_rect[i]=r;
+ r.pos+=Vector2(2,5);
+ r.size-=Vector2(4,7);
+
+
+ if (i==dock_select_rect_over) {
+ dock_select->draw_rect(r,used_selected);
+ } else if (dock_slot[i]->get_child_count()==0) {
+ dock_select->draw_rect(r,unused);
+ } else {
+
+ dock_select->draw_rect(r,used);
+ }
+
+ for(int j=0;j<MIN(3,dock_slot[i]->get_child_count());j++) {
+ int xofs = (r.size.width/3)*j;
+ Color c = used;
+ if (i==dock_popup_selected && (dock_slot[i]->get_current_tab()>3 || dock_slot[i]->get_current_tab()==j))
+ c=tab_selected;
+ dock_select->draw_rect(Rect2(2+ofs.x+xofs,ofs.y,r.size.width/3-1,3),c);
+ }
+
+ }
+}
+
+void EditorNode::_save_docks() {
+
+ Ref<ConfigFile> config;
+ config.instance();
+
+ for(int i=0;i<DOCK_SLOT_MAX;i++) {
+ String names;
+ for(int j=0;j<dock_slot[i]->get_tab_count();j++) {
+ String name = dock_slot[i]->get_tab_control(j)->get_name();
+ if (names!="")
+ names+=",";
+ names+=name;
+ }
+
+ if (names!="") {
+ config->set_value("docks","dock_"+itos(i+1),names);
+ }
+ }
+
+ VSplitContainer*splits[DOCK_SLOT_MAX/2]={
+ left_l_vsplit,
+ left_r_vsplit,
+ right_l_vsplit,
+ right_r_vsplit,
+ };
+
+ for(int i=0;i<DOCK_SLOT_MAX/2;i++) {
+
+ if (splits[i]->is_visible()) {
+ config->set_value("docks","dock_split_"+itos(i+1),splits[i]->get_split_offset());
+ }
+ }
+
+
+ HSplitContainer *h_splits[4]={
+ left_l_hsplit,
+ left_r_hsplit,
+ main_hsplit,
+ right_hsplit,
+ };
+
+ for(int i=0;i<4;i++) {
+
+ config->set_value("docks","dock_hsplit_"+itos(i+1),h_splits[i]->get_split_offset());
+ }
+
+ editor_data.get_plugin_window_layout(config);
+
+ config->save(EditorSettings::get_singleton()->get_project_settings_path().plus_file("editor_layout.cfg"));
+
+}
+
+void EditorNode::save_layout() {
+
+ dock_drag_timer->start();
+}
+
+void EditorNode::_dock_split_dragged(int ofs) {
+
+ dock_drag_timer->start();
+}
+
+void EditorNode::_load_docks() {
+
+ Ref<ConfigFile> config;
+ config.instance();
+ Error err = config->load(EditorSettings::get_singleton()->get_project_settings_path().plus_file("editor_layout.cfg"));
+ if (err!=OK) {
+ return; //no config
+ }
+
+ for(int i=0;i<DOCK_SLOT_MAX;i++) {
+
+ if (!config->has_section_key("docks","dock_"+itos(i+1)))
+ continue;
+
+ Vector<String> names = String(config->get_value("docks","dock_"+itos(i+1))).split(",");
+
+ for(int j=0;j<names.size();j++) {
+
+ String name=names[j];
+ //find it, in a horribly inefficient way
+ int atidx=-1;
+ Control *node=NULL;
+ for(int k=0;k<DOCK_SLOT_MAX;k++) {
+ if (!dock_slot[k]->has_node(name))
+ continue;
+ node=dock_slot[k]->get_node(name)->cast_to<Control>();
+ if (!node)
+ continue;
+ atidx=k;
+ break;
+ }
+ if (atidx==-1) //well, it's not anywhere
+ continue;
+
+ if (atidx==j) {
+ node->raise();
+ continue;
+ }
+ dock_slot[atidx]->remove_child(node);
+
+ if (dock_slot[atidx]->get_tab_count()==0) {
+ dock_slot[atidx]->hide();
+
+ }
+ dock_slot[i]->add_child(node);
+ dock_slot[i]->show();
+ }
+
+ }
+
+ VSplitContainer*splits[DOCK_SLOT_MAX/2]={
+ left_l_vsplit,
+ left_r_vsplit,
+ right_l_vsplit,
+ right_r_vsplit,
+ };
+
+ for(int i=0;i<DOCK_SLOT_MAX/2;i++) {
+
+ if (!config->has_section_key("docks","dock_split_"+itos(i+1)))
+ continue;
+
+ int ofs = config->get_value("docks","dock_split_"+itos(i+1));
+ splits[i]->set_split_offset(ofs);
+ }
+
+ HSplitContainer *h_splits[4]={
+ left_l_hsplit,
+ left_r_hsplit,
+ main_hsplit,
+ right_hsplit,
+ };
+
+ for(int i=0;i<4;i++) {
+ if (!config->has_section_key("docks","dock_hsplit_"+itos(i+1)))
+ continue;
+ int ofs = config->get_value("docks","dock_hsplit_"+itos(i+1));
+ h_splits[i]->set_split_offset(ofs);
+ }
+
+ for(int i=0;i<DOCK_SLOT_MAX/2;i++) {
+ bool in_use = dock_slot[i*2+0]->get_tab_count() || dock_slot[i*2+1]->get_tab_count();
+ if (in_use)
+ splits[i]->show();
+ else
+ splits[i]->hide();
+ }
+
+ for(int i=0;i<DOCK_SLOT_MAX;i++) {
+
+ if (!dock_slot[i]->is_hidden() && dock_slot[i]->get_tab_count()) {
+ dock_slot[i]->set_current_tab(0);
+ }
+ }
+
+ editor_data.set_plugin_window_layout(config);
+
+}
+
+
+void EditorNode::_scene_tab_script_edited(int p_tab) {
+
+ Ref<Script> script = editor_data.get_scene_root_script(p_tab);
+ if (script.is_valid())
+ edit_resource(script);
+}
+
+void EditorNode::_scene_tab_closed(int p_tab) {
+ current_option = SCENE_TAB_CLOSE;
+ tab_closing = p_tab;
+ if (unsaved_cache) {
+ confirmation->get_ok()->set_text("Yes");
+ //confirmation->get_cancel()->show();
+ confirmation->set_text("Close scene? (Unsaved changes will be lost)");
+ confirmation->popup_centered_minsize();
+ }
+ else {
+ _remove_scene(p_tab);
+ //_update_scene_tabs();
+ }
+
+}
+
+
+void EditorNode::_scene_tab_changed(int p_tab) {
+
+
+ //print_line("set current 1 ");
+ bool unsaved = (saved_version!=editor_data.get_undo_redo().get_version());
+ //print_line("version: "+itos(editor_data.get_undo_redo().get_version())+", saved "+itos(saved_version));
+
+ if (p_tab==editor_data.get_edited_scene())
+ return; //pointless
+
+ uint64_t next_scene_version = editor_data.get_scene_version(p_tab);
+
+
+
+ //print_line("scene tab changed???");
+ editor_data.get_undo_redo().create_action("Switch Scene Tab");
+ editor_data.get_undo_redo().add_do_method(this,"set_current_version",unsaved?saved_version:0);
+ editor_data.get_undo_redo().add_do_method(this,"set_current_scene",p_tab);
+ editor_data.get_undo_redo().add_do_method(scene_tabs,"set_current_tab",p_tab);
+ editor_data.get_undo_redo().add_do_method(this,"set_current_version",next_scene_version==0?editor_data.get_undo_redo().get_version()+1:next_scene_version);
+
+ editor_data.get_undo_redo().add_undo_method(this,"set_current_version",next_scene_version);
+ editor_data.get_undo_redo().add_undo_method(this,"set_current_scene",editor_data.get_edited_scene());
+ editor_data.get_undo_redo().add_undo_method(scene_tabs,"set_current_tab",editor_data.get_edited_scene());
+ editor_data.get_undo_redo().add_undo_method(this,"set_current_version",saved_version);
+ editor_data.get_undo_redo().commit_action();
+
+}
+
+void EditorNode::_toggle_search_bar(bool p_pressed) {
+
+ property_editor->set_use_filter(p_pressed);
+
+ if (p_pressed) {
+
+ search_bar->show();
+ search_box->grab_focus();
+ search_box->select_all();
+ } else {
+
+ search_bar->hide();
+ }
+}
+
+void EditorNode::_clear_search_box() {
+
+ if (search_box->get_text()=="")
+ return;
+
+ search_box->clear();
+ property_editor->update_tree();
+}
+
EditorNode::EditorNode() {
EditorHelp::generate_doc(); //before any editor classes are crated
+ SceneState::set_disable_placeholders(true);
+
+ InputDefault *id = Input::get_singleton()->cast_to<InputDefault>();
+
+ if (id) {
+
+ if (!OS::get_singleton()->has_touchscreen_ui_hint() && Input::get_singleton()) {
+ //only if no touchscreen ui hint, set emulation
+ id->set_emulate_touch(false); //just disable just in case
+ }
+ id->set_custom_mouse_cursor(RES());
+ }
+
singleton=this;
+ last_checked_version=0;
+ changing_scene=false;
FileAccess::set_backup_save(true);
@@ -3210,7 +4573,9 @@ EditorNode::EditorNode() {
EditorSettings::create();
ResourceLoader::set_abort_on_missing_resources(false);
+ FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("file_dialog/show_hidden_files"));
ResourceLoader::set_error_notify_func(this,_load_error_notify);
+ ResourceLoader::set_dependency_error_notify_func(this,_dependency_error_report);
ResourceLoader::set_timestamp_on_load(true);
ResourceSaver::set_timestamp_on_save(true);
@@ -3227,7 +4592,13 @@ EditorNode::EditorNode() {
FileDialog::register_func=_file_dialog_register;
FileDialog::unregister_func=_file_dialog_unregister;
+ EditorFileDialog::get_icon_func=_file_dialog_get_icon;
+ EditorFileDialog::register_func=_editor_file_dialog_register;
+ EditorFileDialog::unregister_func=_editor_file_dialog_unregister;
+
+
editor_import_export = memnew( EditorImportExport );
+ add_child(editor_import_export);
register_exporters();
@@ -3245,11 +4616,14 @@ EditorNode::EditorNode() {
gui_base->set_area_as_parent_rect();
- Ref<Theme> theme( memnew( Theme ) );
+ theme = Ref<Theme>( memnew( Theme ) );
gui_base->set_theme( theme );
editor_register_icons(theme);
editor_register_fonts(theme);
+ //theme->set_icon("folder","EditorFileDialog",Theme::get_default()->get_icon("folder","EditorFileDialog"));
+ //theme->set_color("files_disabled","EditorFileDialog",Color(0,0,0,0.7));
+
String global_font = EditorSettings::get_singleton()->get("global/font");
if (global_font!="") {
Ref<Font> fnt = ResourceLoader::load(global_font);
@@ -3268,6 +4642,8 @@ EditorNode::EditorNode() {
theme->set_stylebox("EditorFocus","EditorStyles",focus_sbt);
+ resource_preview = memnew( EditorResourcePreview );
+ add_child(resource_preview);
progress_dialog = memnew( ProgressDialog );
gui_base->add_child(progress_dialog);
@@ -3275,26 +4651,166 @@ EditorNode::EditorNode() {
gui_base->set_anchor( MARGIN_RIGHT, Control::ANCHOR_END );
gui_base->set_anchor( MARGIN_BOTTOM, Control::ANCHOR_END );
gui_base->set_end( Point2(0,0) );
-
+
main_vbox = memnew( VBoxContainer );
gui_base->add_child(main_vbox);
main_vbox->set_area_as_parent_rect(8);
+#if 0
+ PanelContainer *top_dark_panel = memnew( PanelContainer );
+ Ref<StyleBoxTexture> top_dark_sb;
+ top_dark_sb.instance();;
+ top_dark_sb->set_texture(theme->get_icon("PanelTop","EditorIcons"));
+ for(int i=0;i<4;i++) {
+ top_dark_sb->set_margin_size(Margin(i),3);
+ top_dark_sb->set_default_margin(Margin(i),0);
+ }
+ top_dark_sb->set_expand_margin_size(MARGIN_LEFT,20);
+ top_dark_sb->set_expand_margin_size(MARGIN_RIGHT,20);
+
+ top_dark_panel->add_style_override("panel",top_dark_sb);
+ VBoxContainer *top_dark_vb = memnew( VBoxContainer );
+ main_vbox->add_child(top_dark_panel);
+ top_dark_panel->add_child(top_dark_vb);
+#endif
+
+
+
menu_hb = memnew( HBoxContainer );
main_vbox->add_child(menu_hb);
- main_split = memnew( HSplitContainer );
- main_vbox->add_child(main_split);
- main_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
-
+// top_dark_vb->add_child(scene_tabs);
+ //left
+ left_l_hsplit = memnew( HSplitContainer );
+ main_vbox->add_child(left_l_hsplit);
+
+ left_l_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+
+ left_l_vsplit = memnew( VSplitContainer );
+ left_l_hsplit->add_child(left_l_vsplit);
+ dock_slot[DOCK_SLOT_LEFT_UL]=memnew( TabContainer );
+ left_l_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_UL]);
+ dock_slot[DOCK_SLOT_LEFT_BL]=memnew( TabContainer );
+ left_l_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_BL]);
+ left_l_vsplit->hide();
+ dock_slot[DOCK_SLOT_LEFT_UL]->hide();
+ dock_slot[DOCK_SLOT_LEFT_BL]->hide();
+
+ left_r_hsplit = memnew( HSplitContainer );
+ left_l_hsplit->add_child(left_r_hsplit);
+ left_r_vsplit = memnew( VSplitContainer );
+ left_r_hsplit->add_child(left_r_vsplit);
+ dock_slot[DOCK_SLOT_LEFT_UR]=memnew( TabContainer );
+ left_r_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_UR]);
+ dock_slot[DOCK_SLOT_LEFT_BR]=memnew( TabContainer );
+ left_r_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_BR]);
+ //left_r_vsplit->hide();
+ //dock_slot[DOCK_SLOT_LEFT_UR]->hide();
+ //dock_slot[DOCK_SLOT_LEFT_BR]->hide();
+
+
+ main_hsplit = memnew( HSplitContainer );
+ left_r_hsplit->add_child(main_hsplit);
+ //main_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ VBoxContainer * center_vb = memnew( VBoxContainer);
+ main_hsplit->add_child(center_vb);
+ center_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ center_split = memnew( VSplitContainer );
+ //main_hsplit->add_child(center_split);
+ center_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ center_split->set_collapsed(false);
+ center_vb->add_child(center_split);
+
+ right_hsplit = memnew( HSplitContainer );
+ main_hsplit->add_child(right_hsplit);
+
+ right_l_vsplit = memnew( VSplitContainer );
+ right_hsplit->add_child(right_l_vsplit);
+ dock_slot[DOCK_SLOT_RIGHT_UL]=memnew( TabContainer );
+ right_l_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_UL]);
+ dock_slot[DOCK_SLOT_RIGHT_BL]=memnew( TabContainer );
+ right_l_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_BL]);
+ //right_l_vsplit->hide();
+ //dock_slot[DOCK_SLOT_RIGHT_UL]->hide();
+ //dock_slot[DOCK_SLOT_RIGHT_BL]->hide();
+
+ right_r_vsplit = memnew( VSplitContainer );
+ right_hsplit->add_child(right_r_vsplit);
+ dock_slot[DOCK_SLOT_RIGHT_UR]=memnew( TabContainer );
+ right_r_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_UR]);
+ dock_slot[DOCK_SLOT_RIGHT_BR]=memnew( TabContainer );
+ right_r_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_BR]);
+ right_r_vsplit->hide();
+ dock_slot[DOCK_SLOT_RIGHT_UR]->hide();
+ dock_slot[DOCK_SLOT_RIGHT_BR]->hide();
+
+ left_l_vsplit->connect("dragged",this,"_dock_split_dragged");
+ left_r_vsplit->connect("dragged",this,"_dock_split_dragged");
+ right_l_vsplit->connect("dragged",this,"_dock_split_dragged");
+ right_r_vsplit->connect("dragged",this,"_dock_split_dragged");
+
+ left_l_hsplit->connect("dragged",this,"_dock_split_dragged");
+ left_r_hsplit->connect("dragged",this,"_dock_split_dragged");
+ main_hsplit->connect("dragged",this,"_dock_split_dragged");
+ right_hsplit->connect("dragged",this,"_dock_split_dragged");
+
+
+
+ dock_select_popoup = memnew( PopupPanel );
+ gui_base->add_child(dock_select_popoup);
+ VBoxContainer *dock_vb = memnew( VBoxContainer );
+ dock_select_popoup->add_child(dock_vb);
+
+ HBoxContainer *dock_hb = memnew( HBoxContainer);
+ dock_tab_move_left = memnew( ToolButton );
+ dock_tab_move_left->set_icon(theme->get_icon("Back","EditorIcons"));
+ dock_tab_move_left->set_focus_mode(Control::FOCUS_NONE);
+ dock_tab_move_left->connect("pressed",this,"_dock_move_left");
+ //dock_tab_move_left->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ dock_hb->add_child(dock_tab_move_left);
+ dock_hb->add_spacer();
+ dock_tab_move_right = memnew( ToolButton );
+ dock_tab_move_right->set_icon(theme->get_icon("Forward","EditorIcons"));
+ dock_tab_move_right->set_focus_mode(Control::FOCUS_NONE);
+ dock_tab_move_right->connect("pressed",this,"_dock_move_right");
+
+ //dock_tab_move_right->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ dock_hb->add_child(dock_tab_move_right);
+ dock_vb->add_child(dock_hb);
+
+ dock_select = memnew( Control );
+ dock_select->set_custom_minimum_size(Size2(128,64));
+ dock_select->connect("input_event",this,"_dock_select_input");
+ dock_select->connect("draw",this,"_dock_select_draw");
+ dock_select->connect("mouse_exit",this,"_dock_popup_exit");
+ dock_select->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ dock_vb->add_child(dock_select);
+
+
+ dock_select_popoup->set_child_rect(dock_vb);
+ dock_select_popoup->set_as_minsize();
+ dock_select_rect_over=-1;
+ dock_popup_selected=-1;
+ //dock_select_popoup->set_(Size2(20,20));
+
+ for(int i=0;i<DOCK_SLOT_MAX;i++) {
+ dock_slot[i]->set_custom_minimum_size(Size2(230,220));
+ dock_slot[i]->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ dock_slot[i]->set_popup(dock_select_popoup);
+ dock_slot[i]->connect("pre_popup_pressed",this,"_dock_pre_popup",varray(i));
+
+ //dock_slot[i]->set_tab_align(TabContainer::ALIGN_LEFT);
+ }
- left_split = memnew( VSplitContainer );
- main_split->add_child(left_split);
- left_split->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- left_split->set_collapsed(false);
+ dock_drag_timer = memnew( Timer );
+ add_child(dock_drag_timer);
+ dock_drag_timer->set_wait_time(0.5);
+ dock_drag_timer->set_one_shot(true);
+ dock_drag_timer->connect("timeout",this,"_save_docks");
top_split = memnew( VSplitContainer );
- left_split->add_child(top_split);
+ center_split->add_child(top_split);
top_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
top_split->set_collapsed(true);
@@ -3304,19 +4820,19 @@ EditorNode::EditorNode() {
srt->add_constant_override("separation",0);
- main_editor_tabs = memnew( Tabs );
+/* main_editor_tabs = memnew( Tabs );
main_editor_tabs->connect("tab_changed",this,"_editor_select");
- HBoxContainer *srth = memnew( HBoxContainer );
- srt->add_child( srth );
- EmptyControl *tec = memnew( EmptyControl );
- tec->set_minsize(Size2(100,0));
- tec->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- srth->add_child(tec);
- srth->add_child(main_editor_tabs);
- tec = memnew( EmptyControl );
- tec->set_minsize(Size2(100,0));
- srth->add_child(tec);
- tec->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ main_editor_tabs->set_tab_close_display_policy(Tabs::SHOW_NEVER);
+*/
+ scene_tabs=memnew( Tabs );
+ scene_tabs->add_tab("unsaved");
+ scene_tabs->set_tab_align(Tabs::ALIGN_CENTER);
+ scene_tabs->set_tab_close_display_policy(Tabs::SHOW_ACTIVE_ONLY);
+ scene_tabs->connect("tab_changed",this,"_scene_tab_changed");
+ scene_tabs->connect("right_button_pressed",this,"_scene_tab_script_edited");
+ scene_tabs->connect("tab_close", this, "_scene_tab_closed");
+
+ srt->add_child(scene_tabs);
scene_root_parent = memnew( Panel );
@@ -3338,6 +4854,8 @@ EditorNode::EditorNode() {
scene_root = memnew( Viewport );
+
+
//scene_root_base->add_child(scene_root);
scene_root->set_meta("_editor_disable_input",true);
VisualServer::get_singleton()->viewport_set_hide_scenario(scene_root->get_viewport(),true);
@@ -3363,6 +4881,7 @@ EditorNode::EditorNode() {
animation_panel=pc;
animation_panel->hide();
+
HBoxContainer *animation_hb = memnew( HBoxContainer);
animation_vb->add_child(animation_hb);
@@ -3390,6 +4909,8 @@ EditorNode::EditorNode() {
anim_close->set_pressed_texture( anim_close->get_icon("Close","EditorIcons"));
+
+
PanelContainer *top_region = memnew( PanelContainer );
top_region->add_style_override("panel",gui_base->get_stylebox("hover","Button"));
HBoxContainer *left_menu_hb = memnew( HBoxContainer );
@@ -3411,8 +4932,9 @@ EditorNode::EditorNode() {
prev_scene->set_disabled(true);
//left_menu_hb->add_child( prev_scene );
prev_scene->connect("pressed",this,"_menu_option",make_binds(FILE_OPEN_PREV));
- //gui_base->add_child(prev_scene);
+ gui_base->add_child(prev_scene);
prev_scene->set_pos(Point2(3,24));
+ prev_scene->hide();
Separator *vs=NULL;
@@ -3420,15 +4942,20 @@ EditorNode::EditorNode() {
file_menu->set_tooltip("Operations with scene files.");
p=file_menu->get_popup();
p->add_item("New Scene",FILE_NEW_SCENE);
+ p->add_item("New Inherited Scene..",FILE_NEW_INHERITED_SCENE);
p->add_item("Open Scene..",FILE_OPEN_SCENE,KEY_MASK_CMD+KEY_O);
+ p->add_separator();
p->add_item("Save Scene",FILE_SAVE_SCENE,KEY_MASK_CMD+KEY_S);
p->add_item("Save Scene As..",FILE_SAVE_AS_SCENE,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_S);
p->add_separator();
- p->add_item("Goto Prev. Scene",FILE_OPEN_PREV,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_P);
+ p->add_item("Close Scene",FILE_CLOSE,KEY_MASK_SHIFT+KEY_MASK_CTRL+KEY_W);
+ p->add_separator();
+ p->add_item("Close Goto Prev. Scene",FILE_OPEN_PREV,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_P);
p->add_submenu_item("Open Recent","RecentScenes",FILE_OPEN_RECENT);
p->add_separator();
p->add_item("Quick Open Scene..",FILE_QUICK_OPEN_SCENE,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_O);
p->add_item("Quick Open Script..",FILE_QUICK_OPEN_SCRIPT,KEY_MASK_ALT+KEY_MASK_CMD+KEY_O);
+ p->add_item("Quick Search File..",FILE_QUICK_OPEN_FILE,KEY_MASK_ALT+KEY_MASK_CMD+KEY_P);
p->add_separator();
PopupMenu *pm_export = memnew(PopupMenu );
@@ -3450,6 +4977,8 @@ EditorNode::EditorNode() {
p->add_separator();
p->add_item("Project Settings",RUN_SETTINGS);
p->add_separator();
+ p->add_item("Revert Scene",EDIT_REVERT);
+ p->add_separator();
p->add_item("Quit to Project List",RUN_PROJECT_MANAGER,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_Q);
p->add_item("Quit",FILE_QUIT,KEY_MASK_CMD+KEY_Q);
@@ -3458,6 +4987,18 @@ EditorNode::EditorNode() {
p->add_child(recent_scenes);
recent_scenes->connect("item_pressed",this,"_open_recent_scene");
+ {
+ Control *sp = memnew( Control );
+ sp->set_custom_minimum_size(Size2(30,0));
+ menu_hb->add_child(sp);
+ }
+
+ PanelContainer *editor_region = memnew( PanelContainer );
+ editor_region->add_style_override("panel",gui_base->get_stylebox("hover","Button"));
+ main_editor_button_vb = memnew( HBoxContainer );
+ editor_region->add_child(main_editor_button_vb);
+ menu_hb->add_child(editor_region);
+
//menu_hb->add_spacer();
#if 0
node_menu = memnew( MenuButton );
@@ -3494,8 +5035,6 @@ EditorNode::EditorNode() {
left_menu_hb->add_child( import_menu );
p=import_menu->get_popup();
- p->add_item("Sub-Scene",FILE_IMPORT_SUBSCENE);
- p->add_separator();
p->connect("item_pressed",this,"_menu_option");
export_button = memnew( ToolButton );
@@ -3533,18 +5072,18 @@ EditorNode::EditorNode() {
play_button->set_icon(gui_base->get_icon("MainPlay","EditorIcons"));
play_button->set_focus_mode(Control::FOCUS_NONE);
play_button->connect("pressed", this,"_menu_option",make_binds(RUN_PLAY));
- play_button->set_tooltip("Start the scene (F5).");
+ play_button->set_tooltip("Play the project (F5).");
- pause_button = memnew( ToolButton );
+ /*pause_button = memnew( ToolButton );
//menu_panel->add_child(pause_button); - not needed for now?
pause_button->set_toggle_mode(true);
pause_button->set_icon(gui_base->get_icon("Pause","EditorIcons"));
pause_button->set_focus_mode(Control::FOCUS_NONE);
pause_button->connect("pressed", this,"_menu_option",make_binds(RUN_PAUSE));
pause_button->set_tooltip("Pause the scene (F7).");
-
+*/
stop_button = memnew( ToolButton );
play_hb->add_child(stop_button);
//stop_button->set_toggle_mode(true);
@@ -3560,8 +5099,9 @@ EditorNode::EditorNode() {
menu_hb->add_child(native_play_button);
native_play_button->hide();
native_play_button->get_popup()->connect("item_pressed",this,"_run_in_device");
+ run_native->connect("native_run",this,"_menu_option",varray(RUN_PLAY_NATIVE));
- VSeparator *s1 = memnew( VSeparator );
+// VSeparator *s1 = memnew( VSeparator );
// play_hb->add_child(s1);
play_scene_button = memnew( ToolButton );
@@ -3580,30 +5120,34 @@ EditorNode::EditorNode() {
play_custom_scene_button->connect("pressed", this,"_menu_option",make_binds(RUN_PLAY_CUSTOM_SCENE));
play_custom_scene_button->set_tooltip("Play custom scene ("+keycode_get_string(KEY_MASK_CMD|KEY_MASK_SHIFT|KEY_F5)+").");
- fileserver_menu = memnew( MenuButton );
- play_hb->add_child(fileserver_menu);
- fileserver_menu->set_flat(true);
- fileserver_menu->set_focus_mode(Control::FOCUS_NONE);
- fileserver_menu->set_icon(gui_base->get_icon("FileServer","EditorIcons"));
- //fileserver_menu->connect("pressed", this,"_menu_option",make_binds(RUN_PLAY_CUSTOM_SCENE));
- fileserver_menu->set_tooltip("Serve the project filesystem to remote clients.");
-
- p=fileserver_menu->get_popup();
- p->add_check_item("Enable File Server",RUN_FILE_SERVER);
- p->set_item_tooltip(p->get_item_index(RUN_FILE_SERVER),"Enable/Disable the File Server.");
+ debug_button = memnew( MenuButton );
+ debug_button->set_flat(true);
+ play_hb->add_child(debug_button);
+ //debug_button->set_toggle_mode(true);
+ debug_button->set_focus_mode(Control::FOCUS_NONE);
+ debug_button->set_icon(gui_base->get_icon("Remote","EditorIcons"));
+ //debug_button->connect("pressed", this,"_menu_option",make_binds(RUN_LIVE_DEBUG));
+ debug_button->set_tooltip("Debug Options");
+
+ p=debug_button->get_popup();
+ p->add_check_item("Live Editing",RUN_LIVE_DEBUG);
+ p->add_check_item("File Server",RUN_FILE_SERVER);
+ p->add_separator();
+ p->add_check_item("Deploy Remote Debug",RUN_DEPLOY_REMOTE_DEBUG);
+ p->add_check_item("Deploy File Server Clients",RUN_DEPLOY_DUMB_CLIENTS);
p->add_separator();
- p->add_check_item("Deploy Dumb Clients",RUN_DEPLOY_DUMB_CLIENTS);
- //p->set_item_checked( p->get_item_index(RUN_DEPLOY_DUMB_CLIENTS),true );
- p->set_item_tooltip(p->get_item_index(RUN_DEPLOY_DUMB_CLIENTS),"Deploy dumb clients when the File Server is active.");
+ p->add_check_item("Visible Collision Shapes",RUN_DEBUG_COLLISONS);
+ p->add_check_item("Visible Navigation",RUN_DEBUG_NAVIGATION);
p->connect("item_pressed",this,"_menu_option");
+ /*
run_settings_button = memnew( ToolButton );
//menu_hb->add_child(run_settings_button);
//run_settings_button->set_toggle_mode(true);
run_settings_button->set_focus_mode(Control::FOCUS_NONE);
run_settings_button->set_icon(gui_base->get_icon("Run","EditorIcons"));
run_settings_button->connect("pressed", this,"_menu_option",make_binds(RUN_SCENE_SETTINGS));
-
+*/
/*
run_settings_button = memnew( ToolButton );
@@ -3615,6 +5159,39 @@ EditorNode::EditorNode() {
*/
+ progress_hb = memnew( BackgroundProgress );
+ menu_hb->add_child(progress_hb);
+
+ {
+ Control *sp = memnew( Control );
+ sp->set_custom_minimum_size(Size2(30,0));
+ menu_hb->add_child(sp);
+ }
+
+
+ PanelContainer *vu_cont = memnew( PanelContainer );
+ vu_cont->add_style_override("panel",gui_base->get_stylebox("hover","Button"));
+ menu_hb->add_child(vu_cont);
+
+ audio_vu = memnew( TextureProgress );
+ CenterContainer *vu_cc = memnew( CenterContainer );
+ vu_cc->add_child(audio_vu);
+ vu_cont->add_child(vu_cc);
+ audio_vu->set_under_texture(gui_base->get_icon("VuEmpty","EditorIcons"));
+ audio_vu->set_progress_texture(gui_base->get_icon("VuFull","EditorIcons"));
+ audio_vu->set_max(24);
+ audio_vu->set_min(-80);
+ audio_vu->set_step(0.01);
+ audio_vu->set_val(0);
+
+ {
+ Control *sp = memnew( Control );
+ sp->set_custom_minimum_size(Size2(30,0));
+ menu_hb->add_child(sp);
+ }
+
+
+
top_region = memnew( PanelContainer );
top_region->add_style_override("panel",gui_base->get_stylebox("hover","Button"));
HBoxContainer *right_menu_hb = memnew( HBoxContainer );
@@ -3646,6 +5223,15 @@ EditorNode::EditorNode() {
sources_button->connect("pressed",this,"_menu_option",varray(SOURCES_REIMPORT));
sources_button->set_tooltip("Alerts when an external resource has changed.");
+ update_menu = memnew( MenuButton );
+ update_menu->set_tooltip("Spins when the editor window repaints!");
+ right_menu_hb->add_child(update_menu);
+ update_menu->set_icon(gui_base->get_icon("Progress1","EditorIcons"));
+ p=update_menu->get_popup();
+ p->add_check_item("Update Always",SETTINGS_UPDATE_ALWAYS);
+ p->add_check_item("Update Changes",SETTINGS_UPDATE_CHANGES);
+ p->set_item_checked(1,true);
+
//sources_button->connect();
/*
@@ -3657,68 +5243,84 @@ EditorNode::EditorNode() {
- editor_hsplit = memnew( HSplitContainer );
- main_split->add_child(editor_hsplit);
- editor_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ //editor_hsplit = memnew( HSplitContainer );
+ //main_split->add_child(editor_hsplit);
+ //editor_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- editor_vsplit = memnew( VSplitContainer );
- editor_hsplit->add_child(editor_vsplit);
+ //editor_vsplit = memnew( VSplitContainer );
+ //editor_hsplit->add_child(editor_vsplit);
- top_pallete = memnew( TabContainer );
+ //top_pallete = memnew( TabContainer );
scene_tree_dock = memnew( SceneTreeDock(this,scene_root,editor_selection,editor_data) );
scene_tree_dock->set_name("Scene");
- top_pallete->add_child(scene_tree_dock);
-
+ //top_pallete->add_child(scene_tree_dock);
+ dock_slot[DOCK_SLOT_LEFT_UR]->add_child(scene_tree_dock);
+#if 0
resources_dock = memnew( ResourcesDock(this) );
resources_dock->set_name("Resources");
- top_pallete->add_child(resources_dock);
- top_pallete->set_v_size_flags(Control::SIZE_EXPAND_FILL);
-
- EmptyControl *editor_spacer = memnew( EmptyControl );
- editor_spacer->set_minsize(Size2(260,200));
+ //top_pallete->add_child(resources_dock);
+ dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(resources_dock);
+ //top_pallete->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+#endif
+ dock_slot[DOCK_SLOT_RIGHT_BL]->hide();
+ /*Control *editor_spacer = memnew( Control );
+ editor_spacer->set_custom_minimum_size(Size2(260,200));
editor_spacer->set_v_size_flags(Control::SIZE_EXPAND_FILL);
editor_vsplit->add_child( editor_spacer );
editor_spacer->add_child( top_pallete );
- top_pallete->set_area_as_parent_rect();
+ top_pallete->set_area_as_parent_rect();*/
- prop_pallete = memnew( TabContainer );
+ //prop_pallete = memnew( TabContainer );
- prop_pallete->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ //prop_pallete->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- editor_spacer = memnew( EmptyControl );
- editor_spacer->set_minsize(Size2(260,200));
+ /*editor_spacer = memnew( Control );
+ editor_spacer->set_custom_minimum_size(Size2(260,200));
editor_spacer->set_v_size_flags(Control::SIZE_EXPAND_FILL);
editor_vsplit->add_child( editor_spacer );
editor_spacer->add_child( prop_pallete );
- prop_pallete->set_area_as_parent_rect();
+ prop_pallete->set_area_as_parent_rect();*/
VBoxContainer *prop_editor_base = memnew( VBoxContainer );
prop_editor_base->set_name("Inspector"); // Properties?
- prop_pallete->add_child(prop_editor_base);
+ dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(prop_editor_base);
HBoxContainer *prop_editor_hb = memnew( HBoxContainer );
- prop_editor_base->add_child(prop_editor_hb);
- editor_path = memnew( EditorPath(&editor_history) );
- editor_path->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- prop_editor_hb->add_child(editor_path);
- property_editor = memnew( PropertyEditor );
- property_editor->set_autoclear(true);
- property_editor->set_show_categories(true);
- property_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
-
- property_editor->hide_top_label();
-
- prop_editor_base->add_child( property_editor );
- property_editor->set_undo_redo(&editor_data.get_undo_redo());
+ prop_editor_base->add_child(prop_editor_hb);
+ resource_new_button = memnew( ToolButton );
+ resource_new_button->set_tooltip("Create a new resource in memory and edit it");
+ resource_new_button->set_icon(gui_base->get_icon("New","EditorIcons"));
+ prop_editor_hb->add_child(resource_new_button);
+ resource_new_button->connect("pressed",this,"_menu_option",varray(RESOURCE_NEW));
+ resource_new_button->set_focus_mode(Control::FOCUS_NONE);
+
+ resource_load_button = memnew( ToolButton );
+ resource_load_button->set_tooltip("Load an existing resource from disk and edit it");
+ resource_load_button->set_icon(gui_base->get_icon("Load","EditorIcons"));
+ prop_editor_hb->add_child(resource_load_button);
+ resource_load_button->connect("pressed",this,"_menu_option",varray(RESOURCE_LOAD));
+ resource_load_button->set_focus_mode(Control::FOCUS_NONE);
+
+ resource_save_button = memnew( MenuButton );
+ resource_save_button->set_tooltip("Save the currently edited resource");
+ resource_save_button->set_icon(gui_base->get_icon("Save","EditorIcons"));
+ prop_editor_hb->add_child(resource_save_button);
+ resource_save_button->get_popup()->add_item("Save",RESOURCE_SAVE);
+ resource_save_button->get_popup()->add_item("Save As..",RESOURCE_SAVE_AS);
+ resource_save_button->get_popup()->connect("item_pressed",this,"_menu_option");
+ resource_save_button->set_focus_mode(Control::FOCUS_NONE);
+ resource_save_button->set_disabled(true);
+
+ prop_editor_hb->add_spacer();
-
property_back = memnew( ToolButton );
property_back->set_icon( gui_base->get_icon("Back","EditorIcons") );
property_back->set_flat(true);
property_back->set_tooltip("Go to the previous edited object in history.");
+ property_back->set_disabled(true);
prop_editor_hb->add_child( property_back );
@@ -3726,28 +5328,89 @@ EditorNode::EditorNode() {
property_forward->set_icon( gui_base->get_icon("Forward","EditorIcons") );
property_forward->set_flat(true);
property_forward->set_tooltip("Go to the next edited object in history.");
+ property_forward->set_disabled(true);
prop_editor_hb->add_child( property_forward );
+
+ editor_history_menu = memnew( MenuButton );
+ editor_history_menu->set_tooltip("History of recently edited objects");
+ editor_history_menu->set_icon( gui_base->get_icon("History","EditorIcons"));
+ prop_editor_hb->add_child(editor_history_menu);
+ editor_history_menu->connect("about_to_show",this,"_prepare_history");
+ editor_history_menu->get_popup()->connect("item_pressed",this,"_select_history");
+
+
+ prop_editor_hb = memnew( HBoxContainer ); //again...
+
+ prop_editor_base->add_child(prop_editor_hb);
+ editor_path = memnew( EditorPath(&editor_history) );
+ editor_path->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ prop_editor_hb->add_child(editor_path);
+
+ search_button = memnew( ToolButton );
+ search_button->set_toggle_mode(true);
+ search_button->set_pressed(false);
+ search_button->set_icon(gui_base->get_icon("Zoom","EditorIcons"));
+ prop_editor_hb->add_child(search_button);
+ search_button->connect("toggled",this,"_toggle_search_bar");
+
object_menu = memnew( MenuButton );
object_menu->set_icon(gui_base->get_icon("Tools","EditorIcons"));
prop_editor_hb->add_child( object_menu );
object_menu->set_tooltip("Object properties.");
+ create_dialog = memnew( CreateDialog );
+ gui_base->add_child(create_dialog);
+ create_dialog->set_base_type("Resource");
+ create_dialog->connect("create",this,"_resource_created");
+
+ search_bar = memnew( HBoxContainer );
+ search_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ prop_editor_base->add_child(search_bar);
+ search_bar->hide();
+
+ l = memnew( Label("Search: ") );
+ search_bar->add_child(l);
+
+ search_box = memnew( LineEdit );
+ search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ search_bar->add_child(search_box);
+
+ ToolButton *clear_button = memnew( ToolButton );
+ clear_button->set_icon(gui_base->get_icon("Close","EditorIcons"));
+ search_bar->add_child(clear_button);
+ clear_button->connect("pressed",this,"_clear_search_box");
+
+ property_editor = memnew( PropertyEditor );
+ property_editor->set_autoclear(true);
+ property_editor->set_show_categories(true);
+ property_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ property_editor->set_use_doc_hints(true);
+
+ property_editor->hide_top_label();
+ property_editor->register_text_enter(search_box);
+
+ prop_editor_base->add_child( property_editor );
+ property_editor->set_undo_redo(&editor_data.get_undo_redo());
+
scenes_dock = memnew( ScenesDock(this) );
scenes_dock->set_name("FileSystem");
- prop_pallete->add_child(scenes_dock);
+ dock_slot[DOCK_SLOT_LEFT_BR]->add_child(scenes_dock);
+ //prop_pallete->add_child(scenes_dock);
scenes_dock->connect("open",this,"open_request");
scenes_dock->connect("instance",this,"_instance_request");
log = memnew( EditorLog );
- left_split->add_child(log);
+ center_split->add_child(log);
log->connect("close_request",this,"_close_messages");
log->connect("show_request",this,"_show_messages");
//left_split->set_dragger_visible(false);
+
+
old_split_ofs=0;
@@ -3760,42 +5423,23 @@ EditorNode::EditorNode() {
animation_vb->add_child(animation_editor);
- left_split->connect("resized",this,"_vp_resized");
+ center_split->connect("resized",this,"_vp_resized");
animation_editor->hide();
- PanelContainer *bottom_pc = memnew( PanelContainer );
- main_vbox->add_child(bottom_pc);
+ /*PanelContainer *bottom_pc = memnew( PanelContainer );
+ srt->add_child(bottom_pc);
bottom_hb = memnew( HBoxContainer );
- bottom_pc->add_child(bottom_hb);
+ bottom_pc->add_child(bottom_hb);*/
- bottom_hb->add_child( log->get_button() );
+ center_vb->add_child( log->get_button() );
log->get_button()->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- progress_hb = memnew( BackgroundProgress );
- bottom_hb->add_child(progress_hb);
+
//progress_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- audio_vu = memnew( TextureProgress );
- CenterContainer *vu_cc = memnew( CenterContainer );
- vu_cc->add_child(audio_vu);
- bottom_hb->add_child(vu_cc);
- audio_vu->set_under_texture(gui_base->get_icon("VuEmpty","EditorIcons"));
- audio_vu->set_progress_texture(gui_base->get_icon("VuFull","EditorIcons"));
- audio_vu->set_max(24);
- audio_vu->set_min(-80);
- audio_vu->set_step(0.01);
- audio_vu->set_val(0);
- update_menu = memnew( MenuButton );
- update_menu->set_tooltip("Spins when the editor window repaints!");
- bottom_hb->add_child(update_menu);
- update_menu->set_icon(gui_base->get_icon("Progress1","EditorIcons"));
- p=update_menu->get_popup();
- p->add_check_item("Update Always",SETTINGS_UPDATE_ALWAYS);
- p->add_check_item("Update Changes",SETTINGS_UPDATE_CHANGES);
- p->set_item_checked(1,true);
/*
animation_menu = memnew( ToolButton );
@@ -3860,7 +5504,11 @@ EditorNode::EditorNode() {
+ dependency_error = memnew( DependencyErrorDialog );
+ gui_base->add_child(dependency_error);
+ dependency_fixer = memnew( DependencyEditor );
+ gui_base->add_child( dependency_fixer );
settings_config_dialog = memnew( EditorSettingsDialog );
gui_base->add_child(settings_config_dialog);
@@ -3894,7 +5542,7 @@ EditorNode::EditorNode() {
about->get_ok()->set_text("Thanks!");
about->set_hide_on_ok(true);
Label *about_text = memnew( Label );
- about_text->set_text(VERSION_FULL_NAME"\n(c) 2008-2014 Juan Linietsky, Ariel Manzur.\n");
+ about_text->set_text(VERSION_FULL_NAME"\n(c) 2008-2015 Juan Linietsky, Ariel Manzur.\n");
about_text->set_pos(Point2(gui_base->get_icon("Logo","EditorIcons")->get_size().width+30,20));
gui_base->add_child(about);
about->add_child(about_text);
@@ -3903,6 +5551,8 @@ EditorNode::EditorNode() {
logo->set_pos(Point2(20,20));
logo->set_texture(gui_base->get_icon("Logo","EditorIcons") );
+ warning = memnew( AcceptDialog );
+ add_child(warning);
@@ -3916,7 +5566,7 @@ EditorNode::EditorNode() {
file_templates->add_filter("*.tpz ; Template Package");
- file = memnew( FileDialog );
+ file = memnew( EditorFileDialog );
gui_base->add_child(file);
file->set_current_dir("res://");
@@ -3990,8 +5640,9 @@ EditorNode::EditorNode() {
editor_import_export->add_import_plugin( Ref<EditorTextureImportPlugin>( memnew(EditorTextureImportPlugin(this,EditorTextureImportPlugin::MODE_TEXTURE_2D) )));
- editor_import_export->add_import_plugin( Ref<EditorTextureImportPlugin>( memnew(EditorTextureImportPlugin(this,EditorTextureImportPlugin::MODE_TEXTURE_3D) )));
editor_import_export->add_import_plugin( Ref<EditorTextureImportPlugin>( memnew(EditorTextureImportPlugin(this,EditorTextureImportPlugin::MODE_ATLAS) )));
+ editor_import_export->add_import_plugin( Ref<EditorTextureImportPlugin>( memnew(EditorTextureImportPlugin(this,EditorTextureImportPlugin::MODE_LARGE) )));
+ editor_import_export->add_import_plugin( Ref<EditorTextureImportPlugin>( memnew(EditorTextureImportPlugin(this,EditorTextureImportPlugin::MODE_TEXTURE_3D) )));
Ref<EditorSceneImportPlugin> _scene_import = memnew(EditorSceneImportPlugin(this) );
Ref<EditorSceneImporterCollada> _collada_import = memnew( EditorSceneImporterCollada);
_scene_import->add_importer(_collada_import);
@@ -4004,19 +5655,17 @@ EditorNode::EditorNode() {
editor_import_export->add_import_plugin( Ref<EditorSampleImportPlugin>( memnew(EditorSampleImportPlugin(this))));
editor_import_export->add_import_plugin( Ref<EditorTranslationImportPlugin>( memnew(EditorTranslationImportPlugin(this))));
-
- for(int i=0;i<editor_import_export->get_import_plugin_count();i++) {
- import_menu->get_popup()->add_item(editor_import_export->get_import_plugin(i)->get_visible_name(),IMPORT_PLUGIN_BASE+i);
- }
-
editor_import_export->add_export_plugin( Ref<EditorTextureExportPlugin>( memnew(EditorTextureExportPlugin)));
+ editor_import_export->add_export_plugin( Ref<EditorSampleExportPlugin>( memnew(EditorSampleExportPlugin)));
add_editor_plugin( memnew( CanvasItemEditorPlugin(this) ) );
add_editor_plugin( memnew( SpatialEditorPlugin(this) ) );
add_editor_plugin( memnew( ScriptEditorPlugin(this) ) );
- add_editor_plugin( memnew( EditorHelpPlugin(this) ) );
add_editor_plugin( memnew( AnimationPlayerEditorPlugin(this) ) );
- add_editor_plugin( memnew( ShaderEditorPlugin(this) ) );
+ add_editor_plugin( memnew( ShaderGraphEditorPlugin(this,true) ) );
+ add_editor_plugin( memnew( ShaderGraphEditorPlugin(this,false) ) );
+ add_editor_plugin( memnew( ShaderEditorPlugin(this,true) ) );
+ add_editor_plugin( memnew( ShaderEditorPlugin(this,false) ) );
add_editor_plugin( memnew( CameraEditorPlugin(this) ) );
add_editor_plugin( memnew( SampleEditorPlugin(this) ) );
add_editor_plugin( memnew( SampleLibraryEditorPlugin(this) ) );
@@ -4031,28 +5680,39 @@ EditorNode::EditorNode() {
add_editor_plugin( memnew( ParticlesEditorPlugin(this) ) );
add_editor_plugin( memnew( ResourcePreloaderEditorPlugin(this) ) );
add_editor_plugin( memnew( ItemListEditorPlugin(this) ) );
- add_editor_plugin( memnew( RichTextEditorPlugin(this) ) );
+ //add_editor_plugin( memnew( RichTextEditorPlugin(this) ) );
add_editor_plugin( memnew( CollisionPolygonEditorPlugin(this) ) );
add_editor_plugin( memnew( CollisionPolygon2DEditorPlugin(this) ) );
add_editor_plugin( memnew( TileSetEditorPlugin(this) ) );
add_editor_plugin( memnew( TileMapEditorPlugin(this) ) );
add_editor_plugin( memnew( SpriteFramesEditorPlugin(this) ) );
+ add_editor_plugin( memnew( SpriteRegionEditorPlugin(this) ) );
add_editor_plugin( memnew( Particles2DEditorPlugin(this) ) );
add_editor_plugin( memnew( Path2DEditorPlugin(this) ) );
add_editor_plugin( memnew( PathEditorPlugin(this) ) );
add_editor_plugin( memnew( BakedLightEditorPlugin(this) ) );
add_editor_plugin( memnew( Polygon2DEditorPlugin(this) ) );
+ add_editor_plugin( memnew( LightOccluder2DEditorPlugin(this) ) );
+ add_editor_plugin( memnew( NavigationPolygonEditorPlugin(this) ) );
+ add_editor_plugin( memnew( ColorRampEditorPlugin(this) ) );
+ add_editor_plugin( memnew( CollisionShape2DEditorPlugin(this) ) );
for(int i=0;i<EditorPlugins::get_plugin_count();i++)
add_editor_plugin( EditorPlugins::create(i,this) );
+
+ resource_preview->add_preview_generator( Ref<EditorTexturePreviewPlugin>( memnew(EditorTexturePreviewPlugin )));
+ resource_preview->add_preview_generator( Ref<EditorPackedScenePreviewPlugin>( memnew(EditorPackedScenePreviewPlugin )));
+ resource_preview->add_preview_generator( Ref<EditorMaterialPreviewPlugin>( memnew(EditorMaterialPreviewPlugin )));
+ resource_preview->add_preview_generator( Ref<EditorScriptPreviewPlugin>( memnew(EditorScriptPreviewPlugin )));
+ resource_preview->add_preview_generator( Ref<EditorSamplePreviewPlugin>( memnew(EditorSamplePreviewPlugin )));
+ resource_preview->add_preview_generator( Ref<EditorMeshPreviewPlugin>( memnew(EditorMeshPreviewPlugin )));
+
circle_step_msec=OS::get_singleton()->get_ticks_msec();
circle_step_frame=OS::get_singleton()->get_frames_drawn();;
circle_step=0;
-
- import_menu->get_popup()->add_separator();
- import_menu->get_popup()->add_item("Re-Import..",SETTINGS_IMPORT);
+ _rebuild_import_menu();
editor_plugin_screen=NULL;
editor_plugin_over=NULL;
@@ -4067,9 +5727,9 @@ EditorNode::EditorNode() {
Globals::get_singleton()->set("debug/indicators_enabled",true);
Globals::get_singleton()->set("render/room_cull_enabled",false);
- theme->set_color("prop_category","Editor",Color::hex(0x3f3945ff));
- theme->set_color("prop_section","Editor",Color::hex(0x38323dff));
- theme->set_color("prop_subsection","Editor",Color::hex(0x342e39ff));
+ theme->set_color("prop_category","Editor",Color::hex(0x3f3a44ff));
+ theme->set_color("prop_section","Editor",Color::hex(0x35313aff));
+ theme->set_color("prop_subsection","Editor",Color::hex(0x312e37ff));
theme->set_color("fg_selected","Editor",Color::html("ffbd8e8e"));
theme->set_color("fg_error","Editor",Color::html("ffbd8e8e"));
@@ -4100,8 +5760,8 @@ EditorNode::EditorNode() {
}
- edited_scene=NULL;
- saved_version=0;
+ //edited_scene=NULL;
+ saved_version=1;
unsaved_cache=true;
_last_instanced_scene=NULL;
@@ -4155,17 +5815,28 @@ EditorNode::EditorNode() {
Node::set_human_readable_collision_renaming(true);
+
+
// Ref<ImageTexture> it = gui_base->get_icon("logo","Icons");
// OS::get_singleton()->set_icon( it->get_data() );
for(int i=0;i<_init_callbacks.size();i++)
_init_callbacks[i]();
+ editor_data.add_edited_scene(-1);
+ editor_data.set_edited_scene(0);
+ _update_scene_tabs();
+
+ _load_docks();
+
+
}
EditorNode::~EditorNode() {
+
+ memdelete( EditorHelp::get_doc_data() );
memdelete(editor_selection);
memdelete(file_server);
EditorSettings::destroy();
diff --git a/tools/editor/editor_node.h b/tools/editor/editor_node.h
index 7b66a7809e..7d8b97688e 100644
--- a/tools/editor/editor_node.h
+++ b/tools/editor/editor_node.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -78,7 +78,7 @@
#include "tools/editor/editor_plugin.h"
#include "fileserver/editor_file_server.h"
-
+#include "editor_resource_preview.h"
@@ -94,6 +94,8 @@
typedef void (*EditorNodeInitCallback)();
+
+
class EditorNode : public Node {
OBJ_TYPE( EditorNode, Node );
@@ -105,9 +107,12 @@ class EditorNode : public Node {
enum MenuOptions {
FILE_NEW_SCENE,
+ FILE_NEW_INHERITED_SCENE,
FILE_OPEN_SCENE,
FILE_SAVE_SCENE,
FILE_SAVE_AS_SCENE,
+ FILE_SAVE_BEFORE_RUN,
+ FILE_SAVE_AND_RUN,
FILE_IMPORT_SUBSCENE,
FILE_EXPORT_PROJECT,
FILE_EXPORT_MESH_LIBRARY,
@@ -119,18 +124,22 @@ class EditorNode : public Node {
FILE_OPEN_OLD_SCENE,
FILE_QUICK_OPEN_SCENE,
FILE_QUICK_OPEN_SCRIPT,
+ FILE_QUICK_OPEN_FILE,
FILE_RUN_SCRIPT,
FILE_OPEN_PREV,
+ FILE_CLOSE,
FILE_QUIT,
FILE_EXTERNAL_OPEN_SCENE,
EDIT_UNDO,
EDIT_REDO,
+ EDIT_REVERT,
RESOURCE_NEW,
RESOURCE_LOAD,
RESOURCE_SAVE,
RESOURCE_SAVE_AS,
RESOURCE_UNREF,
RESOURCE_COPY,
+ RESOURCE_PASTE,
OBJECT_COPY_PARAMS,
OBJECT_PASTE_PARAMS,
OBJECT_UNIQUE_RESOURCES,
@@ -140,12 +149,17 @@ class EditorNode : public Node {
RUN_PAUSE,
RUN_STOP,
RUN_PLAY_SCENE,
+ RUN_PLAY_NATIVE,
RUN_PLAY_CUSTOM_SCENE,
RUN_SCENE_SETTINGS,
RUN_SETTINGS,
RUN_PROJECT_MANAGER,
RUN_FILE_SERVER,
RUN_DEPLOY_DUMB_CLIENTS,
+ RUN_LIVE_DEBUG,
+ RUN_DEBUG_COLLISONS,
+ RUN_DEBUG_NAVIGATION,
+ RUN_DEPLOY_REMOTE_DEBUG,
SETTINGS_UPDATE_ALWAYS,
SETTINGS_UPDATE_CHANGES,
SETTINGS_IMPORT,
@@ -159,25 +173,54 @@ class EditorNode : public Node {
SOURCES_REIMPORT,
DEPENDENCY_LOAD_CHANGED_IMAGES,
DEPENDENCY_UPDATE_IMPORTED,
+ SCENE_TAB_CLOSE,
IMPORT_PLUGIN_BASE=100,
OBJECT_METHOD_BASE=500
};
+ enum DockSlot {
+ DOCK_SLOT_LEFT_UL,
+ DOCK_SLOT_LEFT_BL,
+ DOCK_SLOT_LEFT_UR,
+ DOCK_SLOT_LEFT_BR,
+ DOCK_SLOT_RIGHT_UL,
+ DOCK_SLOT_RIGHT_BL,
+ DOCK_SLOT_RIGHT_UR,
+ DOCK_SLOT_RIGHT_BR,
+ DOCK_SLOT_MAX
+ };
-
- Node *edited_scene; //scene being edited
+ //Node *edited_scene; //scene being edited
Viewport *scene_root; //root of the scene being edited
- Ref<ResourceImportMetadata> scene_import_metadata;
+ //Ref<ResourceImportMetadata> scene_import_metadata;
Control* scene_root_parent;
Control *gui_base;
VBoxContainer *main_vbox;
- HSplitContainer *main_split;
- VSplitContainer *left_split;
+
+ //split
+
+ HSplitContainer *left_l_hsplit;
+ VSplitContainer *left_l_vsplit;
+ HSplitContainer *left_r_hsplit;
+ VSplitContainer *left_r_vsplit;
+ HSplitContainer *main_hsplit;
+ HSplitContainer *right_hsplit;
+ VSplitContainer *right_l_vsplit;
+ VSplitContainer *right_r_vsplit;
+
+ VSplitContainer *center_split;
+
+ //main tabs
+
+ Tabs *scene_tabs;
+ int tab_closing;
+
+
int old_split_ofs;
VSplitContainer *top_split;
HBoxContainer *bottom_hb;
@@ -187,8 +230,9 @@ class EditorNode : public Node {
TextureButton *anim_close;
Panel *menu_panel;
- HSplitContainer *editor_hsplit;
- VSplitContainer *editor_vsplit;
+
+ //HSplitContainer *editor_hsplit;
+ //VSplitContainer *editor_vsplit;
HBoxContainer *menu_hb;
Control *viewport;
MenuButton *file_menu;
@@ -205,35 +249,44 @@ class EditorNode : public Node {
ToolButton *animation_menu;
ToolButton *play_scene_button;
ToolButton *play_custom_scene_button;
+ MenuButton *debug_button;
+ ToolButton *search_button;
TextureProgress *audio_vu;
- MenuButton *fileserver_menu;
+ //MenuButton *fileserver_menu;
TextEdit *load_errors;
AcceptDialog *load_error_dialog;
Control *scene_root_base;
+ Ref<Theme> theme;
PopupMenu *recent_scenes;
Button *property_back;
Button *property_forward;
SceneTreeDock *scene_tree_dock;
- ResourcesDock *resources_dock;
+ //ResourcesDock *resources_dock;
PropertyEditor *property_editor;
ScenesDock *scenes_dock;
EditorRunNative *run_native;
+ HBoxContainer *search_bar;
+ LineEdit *search_box;
+
+ CreateDialog *create_dialog;
+
CallDialog *call_dialog;
ConfirmationDialog *confirmation;
ConfirmationDialog *import_confirmation;
ConfirmationDialog *open_recent_confirmation;
AcceptDialog *accept;
AcceptDialog *about;
+ AcceptDialog *warning;
//OptimizedPresetsDialog *optimized_presets;
EditorSettingsDialog *settings_config_dialog;
RunSettingsDialog *run_settings_dialog;
ProjectSettings *project_settings;
- FileDialog *file;
+ EditorFileDialog *file;
FileDialog *file_templates;
FileDialog *file_export;
FileDialog *file_export_lib;
@@ -244,8 +297,8 @@ class EditorNode : public Node {
String current_path;
MenuButton *update_menu;
ToolButton *sources_button;
- TabContainer *prop_pallete;
- TabContainer *top_pallete;
+ //TabContainer *prop_pallete;
+ //TabContainer *top_pallete;
String defer_load_scene;
String defer_translatable;
String defer_optimize;
@@ -258,12 +311,18 @@ class EditorNode : public Node {
HBoxContainer *animation_panel_hb;
VBoxContainer *animation_vb;
EditorPath *editor_path;
+ ToolButton *resource_new_button;
+ ToolButton *resource_load_button;
+ MenuButton *resource_save_button;
+ MenuButton *editor_history_menu;
AnimationKeyEditor *animation_editor;
EditorLog *log;
CenterContainer *tabs_center;
EditorQuickOpen *quick_open;
EditorQuickOpen *quick_run;
- Tabs *main_editor_tabs;
+
+ HBoxContainer *main_editor_button_vb;
+ Vector<ToolButton*> main_editor_buttons;
Vector<EditorPlugin*> editor_table;
EditorReImportDialog *reimport_dialog;
@@ -272,6 +331,19 @@ class EditorNode : public Node {
ProgressDialog *progress_dialog;
BackgroundProgress *progress_hb;
+ DependencyErrorDialog *dependency_error;
+ DependencyEditor *dependency_fixer;
+
+ TabContainer *dock_slot[DOCK_SLOT_MAX];
+ Rect2 dock_select_rect[DOCK_SLOT_MAX];
+ int dock_select_rect_over;
+ PopupPanel *dock_select_popoup;
+ Control *dock_select;
+ ToolButton *dock_tab_move_left;
+ ToolButton *dock_tab_move_right;
+ int dock_popup_selected;
+ Timer *dock_drag_timer;
+
String _tmp_import_path;
EditorImportExport *editor_import_export;
@@ -282,8 +354,10 @@ class EditorNode : public Node {
bool reference_resource_mem;
bool save_external_resources_mem;
uint64_t saved_version;
+ uint64_t last_checked_version;
bool unsaved_cache;
String open_navigate;
+ bool changing_scene;
uint32_t circle_step_msec;
uint64_t circle_step_frame;
@@ -299,6 +373,7 @@ class EditorNode : public Node {
EditorSelection *editor_selection;
ProjectExport *project_export;
ProjectExportDialog *project_export_settings;
+ EditorResourcePreview *resource_preview;
EditorFileServer *file_server;
@@ -314,6 +389,7 @@ class EditorNode : public Node {
int current_option;
//void _animation_visibility_toggle();
+ void _resource_created();
void _resource_selected(const RES& p_res,const String& p_property="");
void _menu_option(int p_option);
void _menu_confirm_current();
@@ -322,6 +398,9 @@ class EditorNode : public Node {
void _property_editor_forward();
void _property_editor_back();
+ void _select_history(int p_idx);
+ void _prepare_history();
+
void _fs_changed();
void _sources_changed(bool p_exist);
@@ -332,16 +411,19 @@ class EditorNode : public Node {
void _set_scene_metadata();
void _get_scene_metadata();
void _update_title();
+ void _update_scene_tabs();
void _close_messages();
void _show_messages();
void _vp_resized();
+ void _rebuild_import_menu();
+
void _save_scene(String p_file);
void _instance_request(const String& p_path);
- void _property_keyed(const String& p_keyed,const Variant& p_value);
+ void _property_keyed(const String& p_keyed, const Variant& p_value, bool p_advance);
void _transform_keyed(Object *sp,const String& p_sub,const Transform& p_key);
void _update_keying();
@@ -357,7 +439,7 @@ class EditorNode : public Node {
void _add_to_recent_scenes(const String& p_scene);
void _update_recent_scenes();
void _open_recent_scene(int p_idx);
- void _open_recent_scene_confirm();
+ //void _open_recent_scene_confirm();
String _recent_scene;
bool convert_old;
@@ -374,19 +456,38 @@ class EditorNode : public Node {
String import_reload_fn;
Set<FileDialog*> file_dialogs;
+ Set<EditorFileDialog*> editor_file_dialogs;
+
Map<String,Ref<Texture> > icon_type_cache;
static Ref<Texture> _file_dialog_get_icon(const String& p_path);
static void _file_dialog_register(FileDialog *p_dialog);
static void _file_dialog_unregister(FileDialog *p_dialog);
+ static void _editor_file_dialog_register(EditorFileDialog *p_dialog);
+ static void _editor_file_dialog_unregister(EditorFileDialog *p_dialog);
void _cleanup_scene();
+ void _remove_edited_scene();
+ void _remove_scene(int index);
+ bool _find_and_save_resource(RES p_res,Map<RES,bool>& processed,int32_t flags);
+ bool _find_and_save_edited_subresources(Object *obj,Map<RES,bool>& processed,int32_t flags);
+ void _save_edited_subresources(Node* scene,Map<RES,bool>& processed,int32_t flags);
- bool _find_and_save_edited_subresources(Object *obj,Set<RES>& processed,int32_t flags);
- void _save_edited_subresources(Node* scene,Set<RES>& processed,int32_t flags);
+ void _find_node_types(Node* p_node, int&count_2d, int&count_3d);
+ void _save_scene_with_preview(String p_file);
+ Map<String,Set<String> > dependency_errors;
+
+ static void _dependency_error_report(void *ud,const String& p_path,const String& p_dep,const String& p_type) {
+ EditorNode*en=(EditorNode*)ud;
+ if (!en->dependency_errors.has(p_path))
+ en->dependency_errors[p_path]=Set<String>();
+ en->dependency_errors[p_path].insert(p_dep+"::"+p_type);
+
+ }
+
struct ExportDefer {
String platform;
String path;
@@ -401,6 +502,27 @@ class EditorNode : public Node {
bool _find_scene_in_use(Node* p_node,const String& p_path) const;
+ void _dock_select_input(const InputEvent& p_input);
+ void _dock_move_left();
+ void _dock_move_right();
+ void _dock_select_draw();
+ void _dock_pre_popup(int p_which);
+ void _dock_split_dragged(int ofs);
+ void _dock_popup_exit();
+ void _scene_tab_changed(int p_tab);
+ void _scene_tab_closed(int p_tab);
+ void _scene_tab_script_edited(int p_tab);
+
+ Dictionary _get_main_scene_state();
+ void _set_main_scene_state(Dictionary p_state);
+
+ int _get_current_main_editor();
+
+ void _save_docks();
+ void _load_docks();
+
+ void _toggle_search_bar(bool p_pressed);
+ void _clear_search_box();
protected:
void _notification(int p_what);
@@ -417,10 +539,15 @@ public:
static void add_editor_plugin(EditorPlugin *p_editor);
static void remove_editor_plugin(EditorPlugin *p_editor);
+ void add_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import);
+ void remove_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import);
+
void edit_node(Node *p_node);
void edit_resource(const Ref<Resource>& p_resource);
void open_resource(const String& p_type="");
+
+ void save_resource_in_path(const Ref<Resource>& p_resource,const String& p_path);
void save_resource(const Ref<Resource>& p_resource);
void save_resource_as(const Ref<Resource>& p_resource);
@@ -432,7 +559,7 @@ public:
void open_request(const String& p_path);
- void set_edited_scene(Node *p_scene);
+ bool is_changing_scene() const;
static EditorLog *get_log() { return singleton->log; }
@@ -445,15 +572,23 @@ public:
void hide_animation_player_editors();
void animation_panel_make_visible(bool p_visible);
- Node *get_edited_scene() { return edited_scene; }
+ void set_edited_scene(Node *p_scene);
+
+ Node *get_edited_scene() { return editor_data.get_edited_scene_root(); }
Viewport *get_scene_root() { return scene_root; } //root of the scene being edited
Error save_optimized_copy(const String& p_scene,const String& p_preset);
+ void fix_dependencies(const String& p_for_file);
void clear_scene() { _cleanup_scene(); }
- Error load_scene(const String& p_scene);
+ Error load_scene(const String& p_scene, bool p_ignore_broken_deps=false, bool p_set_inherited=false);
Error load_resource(const String& p_scene);
+ bool is_scene_open(const String& p_path);
+
+ void set_current_version(uint64_t p_version);
+ void set_current_scene(int p_idx);
+
static EditorData& get_editor_data() { return singleton->editor_data; }
static VSplitContainer *get_top_split() { return singleton->top_split; }
@@ -472,6 +607,12 @@ public:
void stop_child_process();
+ Ref<Theme> get_editor_theme() const { return theme; }
+
+
+ void show_warning(const String& p_text);
+
+
Error export_platform(const String& p_platform, const String& p_path, bool p_debug,const String& p_password,bool p_quit_after=false);
static void register_editor_types();
@@ -494,6 +635,9 @@ public:
bool is_scene_in_use(const String& p_path);
void scan_import_changes();
+
+ void save_layout();
+
EditorNode();
~EditorNode();
void get_singleton(const char* arg1, bool arg2);
diff --git a/tools/editor/editor_path.cpp b/tools/editor/editor_path.cpp
index 83ca04fcab..94e2efe346 100644
--- a/tools/editor/editor_path.cpp
+++ b/tools/editor/editor_path.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -73,7 +73,11 @@ void EditorPath::_notification(int p_what) {
if (obj->cast_to<Resource>()) {
Resource *r = obj->cast_to<Resource>();
- name=r->get_name();
+ if (r->get_path().is_resource_file())
+ name=r->get_path().get_file();
+ else
+ name=r->get_name();
+
if (name=="")
name=r->get_type();
} else if (obj->cast_to<Node>()) {
diff --git a/tools/editor/editor_path.h b/tools/editor/editor_path.h
index effa87b423..2edaeb92a3 100644
--- a/tools/editor/editor_path.h
+++ b/tools/editor/editor_path.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/editor_plugin.cpp b/tools/editor/editor_plugin.cpp
index 2a2ad63d32..7417d707bb 100644
--- a/tools/editor/editor_plugin.cpp
+++ b/tools/editor/editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -73,6 +73,13 @@ void EditorPlugin::add_custom_control(CustomControlContainer p_location,Control
} break;
case CONTAINER_CANVAS_EDITOR_SIDE: {
+ CanvasItemEditor::get_singleton()->get_palette_split()->add_child(p_control);
+ CanvasItemEditor::get_singleton()->get_palette_split()->move_child(p_control,0);
+
+ } break;
+ case CONTAINER_CANVAS_EDITOR_BOTTOM: {
+
+ CanvasItemEditor::get_singleton()->get_bottom_split()->add_child(p_control);
} break;
@@ -191,6 +198,13 @@ bool EditorPlugin::get_remove_list(List<Node*> *p_list) {
void EditorPlugin::restore_global_state() {}
void EditorPlugin::save_global_state() {}
+void EditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
+
+}
+
+void EditorPlugin::get_window_layout(Ref<ConfigFile> p_layout){
+
+}
void EditorPlugin::_bind_methods() {
diff --git a/tools/editor/editor_plugin.h b/tools/editor/editor_plugin.h
index bcde0f73fb..0f3a1e2e3c 100644
--- a/tools/editor/editor_plugin.h
+++ b/tools/editor/editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,7 +32,7 @@
#include "scene/main/node.h"
#include "scene/resources/texture.h"
#include "undo_redo.h"
-
+#include "io/config_file.h"
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
@@ -66,7 +66,8 @@ public:
CONTAINER_SPATIAL_EDITOR_SIDE,
CONTAINER_SPATIAL_EDITOR_BOTTOM,
CONTAINER_CANVAS_EDITOR_MENU,
- CONTAINER_CANVAS_EDITOR_SIDE
+ CONTAINER_CANVAS_EDITOR_SIDE,
+ CONTAINER_CANVAS_EDITOR_BOTTOM
};
//TODO: send a resoucre for editing to the editor node?
@@ -89,6 +90,8 @@ public:
virtual void apply_changes() ; // if changes are pending in editor, apply them
virtual void get_breakpoints(List<String> *p_breakpoints);
virtual bool get_remove_list(List<Node*> *p_list);
+ virtual void set_window_layout(Ref<ConfigFile> p_layout);
+ virtual void get_window_layout(Ref<ConfigFile> p_layout);
virtual void restore_global_state();
virtual void save_global_state();
diff --git a/tools/editor/editor_reimport_dialog.cpp b/tools/editor/editor_reimport_dialog.cpp
index 034ac58a8e..8842a485b3 100644
--- a/tools/editor/editor_reimport_dialog.cpp
+++ b/tools/editor/editor_reimport_dialog.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,7 +33,7 @@ void EditorReImportDialog::popup_reimport() {
if (EditorFileSystem::get_singleton()->is_scanning()) {
error->set_text("Please wait for scan to complete");
- error->popup_centered(Size2(250,100));
+ error->popup_centered_minsize();
return;
}
@@ -70,7 +70,7 @@ void EditorReImportDialog::popup_reimport() {
if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->get_filename()=="") {
error->set_text("Current scene must be saved to re-import.");
- error->popup_centered(Size2(250,100));
+ error->popup_centered_minsize();
get_ok()->set_text("Re-Import");
get_ok()->set_disabled(true);
return;
@@ -93,7 +93,7 @@ void EditorReImportDialog::ok_pressed() {
if (EditorFileSystem::get_singleton()->is_scanning()) {
error->set_text("Please wait for scan to complete");
- error->popup_centered(Size2(250,100));
+ error->popup_centered_minsize();
return;
}
diff --git a/tools/editor/editor_reimport_dialog.h b/tools/editor/editor_reimport_dialog.h
index a5fac262fc..9726bac805 100644
--- a/tools/editor/editor_reimport_dialog.h
+++ b/tools/editor/editor_reimport_dialog.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/editor_resource_preview.cpp b/tools/editor/editor_resource_preview.cpp
new file mode 100644
index 0000000000..f684b49a2c
--- /dev/null
+++ b/tools/editor/editor_resource_preview.cpp
@@ -0,0 +1,261 @@
+#include "editor_resource_preview.h"
+#include "editor_settings.h"
+#include "os/file_access.h"
+#include "io/resource_loader.h"
+#include "io/resource_saver.h"
+#include "globals.h"
+
+
+Ref<Texture> EditorResourcePreviewGenerator::generate_from_path(const String& p_path) {
+
+ RES res = ResourceLoader::load(p_path);
+ if (!res.is_valid())
+ return res;
+ return generate(res);
+}
+
+EditorResourcePreviewGenerator::EditorResourcePreviewGenerator() {
+
+
+}
+
+
+EditorResourcePreview* EditorResourcePreview::singleton=NULL;
+
+
+void EditorResourcePreview::_thread_func(void *ud) {
+
+ EditorResourcePreview *erp=(EditorResourcePreview*)ud;
+ erp->_thread();
+
+}
+
+
+void EditorResourcePreview::_preview_ready(const String& p_str,const Ref<Texture>& p_texture,ObjectID id,const StringName& p_func,const Variant& p_ud) {
+
+ //print_line("preview is ready");
+ preview_mutex->lock();
+
+ Item item;
+ item.order=order++;
+ item.preview=p_texture;
+ cache[p_str]=item;
+
+ Object *recv = ObjectDB::get_instance(id);
+ if (recv) {
+ recv->call_deferred(p_func,p_str,p_texture,p_ud);
+ }
+
+ preview_mutex->unlock();
+}
+
+Ref<Texture> EditorResourcePreview::_generate_preview(const QueueItem& p_item,const String& cache_base) {
+
+ String type = ResourceLoader::get_resource_type(p_item.path);
+ //print_line("resource type is: "+type);
+
+ if (type=="")
+ return Ref<Texture>(); //could not guess type
+
+ Ref<Texture> generated;
+
+ for(int i=0;i<preview_generators.size();i++) {
+ if (!preview_generators[i]->handles(type))
+ continue;
+ generated = preview_generators[i]->generate_from_path(p_item.path);
+
+ break;
+ }
+
+ if (generated.is_valid()) {
+ //print_line("was generated");
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+ //wow it generated a preview... save cache
+ ResourceSaver::save(cache_base+".png",generated);
+ FileAccess *f=FileAccess::open(cache_base+".txt",FileAccess::WRITE);
+ f->store_line(itos(thumbnail_size));
+ f->store_line(itos(FileAccess::get_modified_time(p_item.path)));
+ f->store_line(FileAccess::get_md5(p_item.path));
+ memdelete(f);
+ } else {
+ //print_line("was not generated");
+
+ }
+
+ return generated;
+}
+
+void EditorResourcePreview::_thread() {
+
+ //print_line("begin thread");
+ while(!exit) {
+
+ //print_line("wait for semaphore");
+ preview_sem->wait();
+ preview_mutex->lock();
+
+ //print_line("blue team go");
+
+ if (queue.size()) {
+
+
+
+ QueueItem item = queue.front()->get();
+ queue.pop_front();
+ preview_mutex->unlock();
+
+ Ref<Texture> texture;
+
+ //print_line("pop from queue "+item.path);
+
+ uint64_t modtime = FileAccess::get_modified_time(item.path);
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+
+ if (cache.has(item.path)) {
+ //already has it because someone loaded it, just let it know it's ready
+ call_deferred("_preview_ready",item.path,cache[item.path].preview,item.id,item.function,item.userdata);
+
+ } else {
+
+
+ String temp_path=EditorSettings::get_singleton()->get_settings_path().plus_file("tmp");
+ String cache_base = Globals::get_singleton()->globalize_path(item.path).md5_text();
+ cache_base = temp_path.plus_file("resthumb-"+cache_base);
+
+ //does not have it, try to load a cached thumbnail
+
+ String file = cache_base+".txt";
+ //print_line("cachetxt at "+file);
+ FileAccess *f=FileAccess::open(file,FileAccess::READ);
+ if (!f) {
+
+ //print_line("generate because not cached");
+
+ //generate
+ texture=_generate_preview(item,cache_base);
+ } else {
+
+ int tsize = f->get_line().to_int64();
+ uint64_t last_modtime = f->get_line().to_int64();
+
+ bool cache_valid = true;
+
+ if (tsize!=thumbnail_size) {
+ cache_valid=false;
+ memdelete(f);
+ } else if (last_modtime!=modtime) {
+
+ String last_md5 = f->get_line();
+ String md5 = FileAccess::get_md5(item.path);
+ memdelete(f);
+
+ if (last_md5!=md5) {
+
+ cache_valid=false;
+ } else {
+ //update modified time
+
+ f=FileAccess::open(file,FileAccess::WRITE);
+ f->store_line(itos(modtime));
+ f->store_line(md5);
+ memdelete(f);
+ }
+ } else {
+ memdelete(f);
+ }
+
+ if (cache_valid) {
+
+ texture = ResourceLoader::load(cache_base+".png","ImageTexture",true);
+ if (!texture.is_valid()) {
+ //well fuck
+ cache_valid=false;
+ }
+ }
+
+ if (!cache_valid) {
+
+ texture=_generate_preview(item,cache_base);
+ }
+
+ }
+
+ //print_line("notify of preview ready");
+ call_deferred("_preview_ready",item.path,texture,item.id,item.function,item.userdata);
+
+ }
+
+ } else {
+ preview_mutex->unlock();
+ }
+
+ }
+}
+
+
+
+
+void EditorResourcePreview::queue_resource_preview(const String& p_path, Object* p_receiver, const StringName& p_receiver_func, const Variant& p_userdata) {
+
+ ERR_FAIL_NULL(p_receiver);
+ preview_mutex->lock();
+ if (cache.has(p_path)) {
+ cache[p_path].order=order++;
+ p_receiver->call_deferred(p_receiver_func,p_path,cache[p_path].preview,p_userdata);
+ preview_mutex->unlock();
+ return;
+
+ }
+
+ //print_line("send to thread "+p_path);
+ QueueItem item;
+ item.function=p_receiver_func;
+ item.id=p_receiver->get_instance_ID();
+ item.path=p_path;
+ item.userdata=p_userdata;
+
+ queue.push_back(item);
+ preview_mutex->unlock();
+ preview_sem->post();
+
+}
+
+void EditorResourcePreview::add_preview_generator(const Ref<EditorResourcePreviewGenerator>& p_generator) {
+
+ preview_generators.push_back(p_generator);
+}
+
+EditorResourcePreview* EditorResourcePreview::get_singleton() {
+
+ return singleton;
+}
+
+void EditorResourcePreview::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_preview_ready",&EditorResourcePreview::_preview_ready);
+}
+
+EditorResourcePreview::EditorResourcePreview() {
+ singleton=this;
+ preview_mutex = Mutex::create();
+ preview_sem = Semaphore::create();
+ order=0;
+ exit=false;
+
+ thread = Thread::create(_thread_func,this);
+}
+
+
+EditorResourcePreview::~EditorResourcePreview()
+{
+
+ exit=true;
+ preview_sem->post();
+ Thread::wait_to_finish(thread);
+ memdelete(thread);
+ memdelete(preview_mutex);
+ memdelete(preview_sem);
+
+
+}
+
diff --git a/tools/editor/editor_resource_preview.h b/tools/editor/editor_resource_preview.h
new file mode 100644
index 0000000000..aadae75597
--- /dev/null
+++ b/tools/editor/editor_resource_preview.h
@@ -0,0 +1,95 @@
+#ifndef EDITORRESOURCEPREVIEW_H
+#define EDITORRESOURCEPREVIEW_H
+
+#include "scene/main/node.h"
+#include "os/semaphore.h"
+#include "os/thread.h"
+#include "scene/resources/texture.h"
+
+/* make previews for:
+*packdscene
+*wav
+*image
+*mesh
+-font
+*script
+*material
+-shader
+-shader graph?
+-navigation mesh
+-collision?
+-occluder polygon
+-navigation polygon
+-tileset
+-curve and curve2D
+*/
+
+
+class EditorResourcePreviewGenerator : public Reference {
+
+ OBJ_TYPE(EditorResourcePreviewGenerator,Reference );
+
+public:
+
+ virtual bool handles(const String& p_type) const=0;
+ virtual Ref<Texture> generate(const RES& p_from)=0;
+ virtual Ref<Texture> generate_from_path(const String& p_path);
+
+ EditorResourcePreviewGenerator();
+};
+
+
+class EditorResourcePreview : public Node {
+
+ OBJ_TYPE(EditorResourcePreview,Node);
+
+
+ static EditorResourcePreview* singleton;
+
+ struct QueueItem {
+ String path;
+ ObjectID id;
+ StringName function;
+ Variant userdata;
+ };
+
+ List<QueueItem> queue;
+
+ Mutex *preview_mutex;
+ Semaphore *preview_sem;
+ Thread *thread;
+ bool exit;
+
+ struct Item {
+ Ref<Texture> preview;
+ int order;
+ };
+
+ int order;
+
+ Map<String,Item> cache;
+
+ void _preview_ready(const String& p_str,const Ref<Texture>& p_texture, ObjectID id, const StringName &p_func, const Variant &p_ud);
+ Ref<Texture> _generate_preview(const QueueItem& p_item, const String &cache_base);
+
+ static void _thread_func(void *ud);
+ void _thread();
+
+ Vector<Ref<EditorResourcePreviewGenerator> > preview_generators;
+protected:
+
+ static void _bind_methods();
+public:
+
+ static EditorResourcePreview* get_singleton();
+
+ //callback funtion is callback(String p_path,Ref<Texture> preview,Variant udata) preview null if could not load
+ void queue_resource_preview(const String& p_path, Object* p_receiver, const StringName& p_receiver_func, const Variant& p_userdata);
+
+ void add_preview_generator(const Ref<EditorResourcePreviewGenerator>& p_generator);
+
+ EditorResourcePreview();
+ ~EditorResourcePreview();
+};
+
+#endif // EDITORRESOURCEPREVIEW_H
diff --git a/tools/editor/editor_run.cpp b/tools/editor/editor_run.cpp
index d545b8d8f2..b635cea84b 100644
--- a/tools/editor/editor_run.cpp
+++ b/tools/editor/editor_run.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,7 +28,7 @@
/*************************************************************************/
#include "editor_run.h"
#include "globals.h"
-
+#include "editor_settings.h"
EditorRun::Status EditorRun::get_status() const {
@@ -61,6 +61,77 @@ Error EditorRun::run(const String& p_scene,const String p_custom_args,const List
}
}
+ if (debug_collisions) {
+ args.push_back("-debugcol");
+ }
+
+ if (debug_navigation) {
+ args.push_back("-debugnav");
+ }
+
+ int screen = EditorSettings::get_singleton()->get("game_window_placement/screen");
+
+ if (screen==0) {
+ screen=OS::get_singleton()->get_current_screen();
+ } else {
+ screen--;
+ }
+
+ Rect2 screen_rect;
+ screen_rect.pos=OS::get_singleton()->get_screen_position(screen);
+ screen_rect.size=OS::get_singleton()->get_screen_size(screen);
+
+
+ Size2 desired_size;
+
+ desired_size.x=Globals::get_singleton()->get("display/width");
+ desired_size.y=Globals::get_singleton()->get("display/height");
+
+ Size2 test_size;
+ test_size.x=Globals::get_singleton()->get("display/test_width");
+ test_size.y=Globals::get_singleton()->get("display/test_height");
+ if (test_size.x>0 && test_size.y>0) {
+
+ desired_size=test_size;
+ }
+
+
+ int window_placement=EditorSettings::get_singleton()->get("game_window_placement/rect");
+
+ switch(window_placement) {
+ case 0: { // default
+
+ args.push_back("-p");
+ args.push_back(itos(screen_rect.pos.x)+"x"+itos(screen_rect.pos.y));
+ } break;
+ case 1: { // centered
+ Vector2 pos=screen_rect.pos+((screen_rect.size-desired_size)/2).floor();
+ args.push_back("-p");
+ args.push_back(itos(pos.x)+"x"+itos(pos.y));
+ } break;
+ case 2: { // custom pos
+ Vector2 pos = EditorSettings::get_singleton()->get("game_window_placement/rect_custom_position");
+ pos+=screen_rect.pos;
+ args.push_back("-p");
+ args.push_back(itos(pos.x)+"x"+itos(pos.y));
+ } break;
+ case 3: { // force maximized
+ Vector2 pos=screen_rect.pos;
+ args.push_back("-p");
+ args.push_back(itos(pos.x)+"x"+itos(pos.y));
+ args.push_back("-mx");
+
+ } break;
+ case 4: { // force fullscreen
+
+ Vector2 pos=screen_rect.pos;
+ args.push_back("-p");
+ args.push_back(itos(pos.x)+"x"+itos(pos.y));
+ args.push_back("-f");
+ } break;
+ }
+
+
if (p_breakpoints.size()) {
args.push_back("-bp");
@@ -105,7 +176,31 @@ void EditorRun::stop() {
status=STATUS_STOP;
}
+void EditorRun::set_debug_collisions(bool p_debug) {
+
+ debug_collisions=p_debug;
+}
+
+bool EditorRun::get_debug_collisions() const{
+
+ return debug_collisions;
+}
+
+void EditorRun::set_debug_navigation(bool p_debug) {
+
+ debug_navigation=p_debug;
+}
+
+bool EditorRun::get_debug_navigation() const{
+
+ return debug_navigation;
+}
+
+
EditorRun::EditorRun() {
status=STATUS_STOP;
+ debug_collisions=false;
+ debug_navigation=false;
+
}
diff --git a/tools/editor/editor_run.h b/tools/editor/editor_run.h
index 94a0f1878f..e1b0b081c7 100644
--- a/tools/editor/editor_run.h
+++ b/tools/editor/editor_run.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -43,12 +43,22 @@ public:
OS::ProcessID pid;
private:
+ bool debug_collisions;
+ bool debug_navigation;
Status status;
public:
Status get_status() const;
Error run(const String& p_scene,const String p_custom_args,const List<String>& p_breakpoints,const String& p_edited_scene);
+ void run_native_notify() { status=STATUS_PLAY; }
void stop();
+
+ void set_debug_collisions(bool p_debug);
+ bool get_debug_collisions() const;
+
+ void set_debug_navigation(bool p_debug);
+ bool get_debug_navigation() const;
+
EditorRun();
};
diff --git a/tools/editor/editor_run_native.cpp b/tools/editor/editor_run_native.cpp
index 17117be188..2eedba93dc 100644
--- a/tools/editor/editor_run_native.cpp
+++ b/tools/editor/editor_run_native.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -101,12 +101,28 @@ void EditorRunNative::_run_native(int p_idx,const String& p_platform) {
Ref<EditorExportPlatform> eep = EditorImportExport::get_singleton()->get_export_platform(p_platform);
ERR_FAIL_COND(eep.is_null());
- eep->run(p_idx,deploy_dumb);
+ if (deploy_debug_remote) {
+ emit_signal("native_run");
+
+ }
+ int flags=0;
+ if (deploy_debug_remote)
+ flags|=EditorExportPlatform::EXPORT_REMOTE_DEBUG;
+ if (deploy_dumb)
+ flags|=EditorExportPlatform::EXPORT_DUMB_CLIENT;
+ if (debug_collisions)
+ flags|=EditorExportPlatform::EXPORT_VIEW_COLLISONS;
+ if (debug_navigation)
+ flags|=EditorExportPlatform::EXPORT_VIEW_NAVIGATION;
+
+ eep->run(p_idx,flags);
}
void EditorRunNative::_bind_methods() {
ObjectTypeDB::bind_method("_run_native",&EditorRunNative::_run_native);
+
+ ADD_SIGNAL(MethodInfo("native_run"));
}
void EditorRunNative::set_deploy_dumb(bool p_enabled) {
@@ -119,10 +135,43 @@ bool EditorRunNative::is_deploy_dumb_enabled() const{
return deploy_dumb;
}
+void EditorRunNative::set_deploy_debug_remote(bool p_enabled) {
+
+ deploy_debug_remote=p_enabled;
+}
+
+bool EditorRunNative::is_deploy_debug_remote_enabled() const{
+
+ return deploy_debug_remote;
+}
+
+void EditorRunNative::set_debug_collisions(bool p_debug) {
+
+ debug_collisions=p_debug;
+}
+
+bool EditorRunNative::get_debug_collisions() const{
+
+ return debug_collisions;
+}
+
+void EditorRunNative::set_debug_navigation(bool p_debug) {
+
+ debug_navigation=p_debug;
+}
+
+bool EditorRunNative::get_debug_navigation() const{
+
+ return debug_navigation;
+}
EditorRunNative::EditorRunNative()
{
set_process(true);
first=true;
deploy_dumb=false;
+ deploy_debug_remote=false;
+ debug_collisions=false;
+ debug_navigation=false;
+
}
diff --git a/tools/editor/editor_run_native.h b/tools/editor/editor_run_native.h
index f4bda2d07d..77d6dc198e 100644
--- a/tools/editor/editor_run_native.h
+++ b/tools/editor/editor_run_native.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -39,6 +39,9 @@ class EditorRunNative : public HBoxContainer {
Map<StringName,MenuButton*> menus;
bool first;
bool deploy_dumb;
+ bool deploy_debug_remote;
+ bool debug_collisions;
+ bool debug_navigation;
void _run_native(int p_idx,const String& p_platform);
@@ -50,6 +53,16 @@ public:
void set_deploy_dumb(bool p_enabled);
bool is_deploy_dumb_enabled() const;
+
+ void set_deploy_debug_remote(bool p_enabled);
+ bool is_deploy_debug_remote_enabled() const;
+
+ void set_debug_collisions(bool p_debug);
+ bool get_debug_collisions() const;
+
+ void set_debug_navigation(bool p_debug);
+ bool get_debug_navigation() const;
+
EditorRunNative();
};
diff --git a/tools/editor/editor_run_script.cpp b/tools/editor/editor_run_script.cpp
index 5f8598d052..90581374f6 100644
--- a/tools/editor/editor_run_script.cpp
+++ b/tools/editor/editor_run_script.cpp
@@ -18,7 +18,7 @@ void EditorScript::add_root_node(Node *p_node) {
return;
}
- editor->set_edited_scene(p_node);
+// editor->set_edited_scene(p_node);
}
Node *EditorScript::get_scene() {
diff --git a/tools/editor/editor_selection.cpp b/tools/editor/editor_selection.cpp
index 890b31cd4d..d8fd8735fc 100644
--- a/tools/editor/editor_selection.cpp
+++ b/tools/editor/editor_selection.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/editor_selection.h b/tools/editor/editor_selection.h
index 7f726e2446..f10f313a4e 100644
--- a/tools/editor/editor_selection.h
+++ b/tools/editor/editor_selection.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/editor_settings.cpp b/tools/editor/editor_settings.cpp
index 8b0fd204e3..15de6e7266 100644
--- a/tools/editor/editor_settings.cpp
+++ b/tools/editor/editor_settings.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -224,6 +224,28 @@ void EditorSettings::create() {
dir->change_dir("..");
}
+ if (dir->change_dir("config")!=OK) {
+ dir->make_dir("config");
+ } else {
+
+ dir->change_dir("..");
+ }
+
+ dir->change_dir("config");
+
+ String pcp=Globals::get_singleton()->get_resource_path();
+ if (pcp.ends_with("/"))
+ pcp=config_path.substr(0,pcp.size()-1);
+ pcp=pcp.get_file()+"-"+pcp.md5_text();
+
+ if (dir->change_dir(pcp)) {
+ dir->make_dir(pcp);
+ } else {
+ dir->change_dir("..");
+ }
+
+ dir->change_dir("..");
+
// path at least is validated, so validate config file
@@ -244,6 +266,7 @@ void EditorSettings::create() {
}
singleton->config_file_path=config_file_path;
+ singleton->project_config_path=pcp;
singleton->settings_path=config_path+"/"+config_dir;
if (OS::get_singleton()->is_stdout_verbose()) {
@@ -251,7 +274,8 @@ void EditorSettings::create() {
print_line("EditorSettings: Load OK!");
}
-
+ singleton->setup_network();
+ singleton->load_favorites();
singleton->scan_plugins();
return;
@@ -264,7 +288,9 @@ void EditorSettings::create() {
singleton = Ref<EditorSettings>( memnew( EditorSettings ) );
singleton->config_file_path=config_file_path;
+ singleton->settings_path=config_path+"/"+config_dir;
singleton->_load_defaults();
+ singleton->setup_network();
singleton->scan_plugins();
@@ -306,6 +332,35 @@ Error EditorSettings::_load_plugin(const String& p_path, Plugin &plugin) {
return OK;
}
+void EditorSettings::setup_network() {
+
+ List<IP_Address> local_ip;
+ IP::get_singleton()->get_local_addresses(&local_ip);
+ String lip;
+ String hint;
+ String current=get("network/debug_host");
+
+ for(List<IP_Address>::Element *E=local_ip.front();E;E=E->next()) {
+
+ String ip = E->get();
+ if (ip=="127.0.0.1")
+ continue;
+
+ if (lip!="")
+ lip=ip;
+ if (ip==current)
+ lip=current; //so it saves
+ if (hint!="")
+ hint+=",";
+ hint+=ip;
+
+ }
+
+ set("network/debug_host",lip);
+ add_property_hint(PropertyInfo(Variant::STRING,"network/debug_host",PROPERTY_HINT_ENUM,hint));
+
+}
+
void EditorSettings::scan_plugins() {
Map<String,Plugin> new_plugins;
@@ -392,7 +447,7 @@ void EditorSettings::_load_defaults() {
hints["global/default_project_path"]=PropertyInfo(Variant::STRING,"global/default_project_path",PROPERTY_HINT_GLOBAL_DIR);
set("global/default_project_export_path","");
hints["global/default_project_export_path"]=PropertyInfo(Variant::STRING,"global/default_project_export_path",PROPERTY_HINT_GLOBAL_DIR);
-
+ set("global/show_script_in_scene_tabs",false);
set("text_editor/background_color",Color::html("3b000000"));
set("text_editor/text_color",Color::html("aaaaaa"));
set("text_editor/text_selected_color",Color::html("000000"));
@@ -404,14 +459,22 @@ void EditorSettings::_load_defaults() {
set("text_editor/symbol_color",Color::html("badfff"));
set("text_editor/selection_color",Color::html("7b5dbe"));
set("text_editor/brace_mismatch_color",Color(1,0.2,0.2));
+ set("text_editor/current_line_color",Color(0.3,0.5,0.8,0.15));
set("text_editor/idle_parse_delay",2);
set("text_editor/create_signal_callbacks",true);
- set("text_editor/autosave_interval_seconds",60);
+ set("text_editor/autosave_interval_secs",0);
+
set("text_editor/font","");
hints["text_editor/font"]=PropertyInfo(Variant::STRING,"text_editor/font",PROPERTY_HINT_GLOBAL_FILE,"*.fnt");
set("text_editor/auto_brace_complete", false);
+ set("text_editor/restore_scripts_on_load",true);
+
+ set("scenetree_editor/duplicate_node_name_num_separator",0);
+ hints["scenetree_editor/duplicate_node_name_num_separator"]=PropertyInfo(Variant::INT,"scenetree_editor/duplicate_node_name_num_separator",PROPERTY_HINT_ENUM, "None,Space,Underscore,Dash");
+
+ set("gridmap_editor/pick_distance", 5000.0);
set("3d_editor/default_fov",45.0);
set("3d_editor/default_z_near",0.1);
@@ -419,6 +482,8 @@ void EditorSettings::_load_defaults() {
set("3d_editor/navigation_scheme",0);
hints["3d_editor/navigation_scheme"]=PropertyInfo(Variant::INT,"3d_editor/navigation_scheme",PROPERTY_HINT_ENUM,"Godot,Maya,Modo");
+ set("3d_editor/zoom_style",0);
+ hints["3d_editor/zoom_style"]=PropertyInfo(Variant::INT,"3d_editor/zoom_style",PROPERTY_HINT_ENUM,"Vertical, Horizontal");
set("3d_editor/orbit_modifier",0);
hints["3d_editor/orbit_modifier"]=PropertyInfo(Variant::INT,"3d_editor/orbit_modifier",PROPERTY_HINT_ENUM,"None,Shift,Alt,Meta,Ctrl");
set("3d_editor/pan_modifier",1);
@@ -432,6 +497,16 @@ void EditorSettings::_load_defaults() {
set("2d_editor/bone_selected_color",Color(0.9,0.45,0.45,0.9));
set("2d_editor/bone_ik_color",Color(0.9,0.9,0.45,0.9));
+ set("game_window_placement/rect",0);
+ hints["game_window_placement/rect"]=PropertyInfo(Variant::INT,"game_window_placement/rect",PROPERTY_HINT_ENUM,"Default,Centered,Custom Position,Force Maximized,Force Full Screen");
+ String screen_hints="Default (Same as Editor)";
+ for(int i=0;i<OS::get_singleton()->get_screen_count();i++) {
+ screen_hints+=",Monitor "+itos(i+1);
+ }
+ set("game_window_placement/rect_custom_position",Vector2());
+ set("game_window_placement/screen",0);
+ hints["game_window_placement/screen"]=PropertyInfo(Variant::INT,"game_window_placement/screen",PROPERTY_HINT_ENUM,screen_hints);
+
set("on_save/compress_binary_resources",true);
set("on_save/save_modified_external_resources",true);
set("on_save/save_paths_as_relative",false);
@@ -439,11 +514,15 @@ void EditorSettings::_load_defaults() {
set("text_editor/create_signal_callbacks",true);
+ set("file_dialog/show_hidden_files", false);
+ set("file_dialog/thumbnail_size", 64);
+ hints["file_dialog/thumbnail_size"]=PropertyInfo(Variant::INT,"file_dialog/thumbnail_size",PROPERTY_HINT_RANGE,"32,128,16");
set("animation/autorename_animation_tracks",true);
set("animation/confirm_insert_track",true);
set("property_editor/texture_preview_width",48);
+ set("property_editor/auto_refresh_interval",0.3);
set("help/doc_path","");
set("import/ask_save_before_reimport",false);
@@ -636,6 +715,71 @@ void EditorSettings::set_plugin_enabled(const String& p_plugin, bool p_enabled)
}
+void EditorSettings::set_favorite_dirs(const Vector<String>& p_favorites) {
+
+ favorite_dirs=p_favorites;
+ FileAccess *f = FileAccess::open(get_project_settings_path().plus_file("favorite_dirs"),FileAccess::WRITE);
+ if (f) {
+ for(int i=0;i<favorite_dirs.size();i++)
+ f->store_line(favorite_dirs[i]);
+ memdelete(f);
+ }
+
+}
+
+Vector<String> EditorSettings::get_favorite_dirs() const {
+
+ return favorite_dirs;
+}
+
+
+void EditorSettings::set_recent_dirs(const Vector<String>& p_recent) {
+
+ recent_dirs=p_recent;
+ FileAccess *f = FileAccess::open(get_project_settings_path().plus_file("recent_dirs"),FileAccess::WRITE);
+ if (f) {
+ for(int i=0;i<recent_dirs.size();i++)
+ f->store_line(recent_dirs[i]);
+ memdelete(f);
+ }
+}
+
+Vector<String> EditorSettings::get_recent_dirs() const {
+
+ return recent_dirs;
+}
+
+String EditorSettings::get_project_settings_path() const {
+
+
+ return get_settings_path().plus_file("config").plus_file(project_config_path);
+}
+
+
+void EditorSettings::load_favorites() {
+
+ FileAccess *f = FileAccess::open(get_project_settings_path().plus_file("favorite_dirs"),FileAccess::READ);
+ if (f) {
+ String line = f->get_line().strip_edges();
+ while(line!="") {
+ favorite_dirs.push_back(line);
+ line = f->get_line().strip_edges();
+ }
+ memdelete(f);
+ }
+
+ f = FileAccess::open(get_project_settings_path().plus_file("recent_dirs"),FileAccess::READ);
+ if (f) {
+ String line = f->get_line().strip_edges();
+ while(line!="") {
+ recent_dirs.push_back(line);
+ line = f->get_line().strip_edges();
+ }
+ memdelete(f);
+ }
+
+}
+
void EditorSettings::_bind_methods() {
diff --git a/tools/editor/editor_settings.h b/tools/editor/editor_settings.h
index c44afd9593..4ba940cd1c 100644
--- a/tools/editor/editor_settings.h
+++ b/tools/editor/editor_settings.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -88,6 +88,11 @@ private:
void _load_defaults();
+ String project_config_path;
+
+ Vector<String> favorite_dirs;
+ Vector<String> recent_dirs;
+
protected:
static void _bind_methods();
@@ -102,11 +107,13 @@ public:
static EditorSettings *get_singleton();
void erase(String p_var);
String get_settings_path() const;
+ String get_project_settings_path() const;
const Map<String,Plugin>& get_plugins() const { return plugins; }
void scan_plugins();
void enable_plugins();
+ void setup_network();
void raise_order(const String& p_name);
static void create();
@@ -125,6 +132,14 @@ public:
void add_property_hint(const PropertyInfo& p_hint);
+ void set_favorite_dirs(const Vector<String>& p_favorite_dirs);
+ Vector<String> get_favorite_dirs() const;
+
+ void set_recent_dirs(const Vector<String>& p_recent_dirs);
+ Vector<String> get_recent_dirs() const;
+
+ void load_favorites();
+
EditorSettings();
~EditorSettings();
diff --git a/tools/editor/editor_sub_scene.cpp b/tools/editor/editor_sub_scene.cpp
index 25a52bb2cd..2a6eba2554 100644
--- a/tools/editor/editor_sub_scene.cpp
+++ b/tools/editor/editor_sub_scene.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -215,7 +215,7 @@ EditorSubScene::EditorSubScene() {
tree->set_v_size_flags(SIZE_EXPAND_FILL);
vb->add_margin_child("Import From Node:",tree)->set_v_size_flags(SIZE_EXPAND_FILL);
- file_dialog = memnew( FileDialog );
+ file_dialog = memnew( EditorFileDialog );
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions);
@@ -224,7 +224,7 @@ EditorSubScene::EditorSubScene() {
file_dialog->add_filter("*."+E->get());
}
- file_dialog->set_mode(FileDialog::MODE_OPEN_FILE);
+ file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
add_child(file_dialog);
file_dialog->connect("file_selected",this,"_path_selected");
diff --git a/tools/editor/editor_sub_scene.h b/tools/editor/editor_sub_scene.h
index 8f5e864eb9..dfd6c531e2 100644
--- a/tools/editor/editor_sub_scene.h
+++ b/tools/editor/editor_sub_scene.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,7 +31,7 @@
#include "scene/gui/dialogs.h"
#include "scene/gui/tree.h"
-#include "scene/gui/file_dialog.h"
+#include "tools/editor/editor_file_dialog.h"
class EditorSubScene : public ConfirmationDialog {
@@ -42,7 +42,7 @@ class EditorSubScene : public ConfirmationDialog {
Tree *tree;
Node *scene;
- FileDialog *file_dialog;
+ EditorFileDialog *file_dialog;
void _fill_tree(Node* p_node,TreeItem *p_parent);
void _reown(Node* p_node,List<Node*> *p_to_reown);
diff --git a/tools/editor/editor_vu.cpp b/tools/editor/editor_vu.cpp
index cd0e75f22d..ac11aceb21 100644
--- a/tools/editor/editor_vu.cpp
+++ b/tools/editor/editor_vu.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/editor_vu.h b/tools/editor/editor_vu.h
index f8fef8b510..4c51ac31c0 100644
--- a/tools/editor/editor_vu.h
+++ b/tools/editor/editor_vu.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/file_type_cache.cpp b/tools/editor/file_type_cache.cpp
index f00fc8fe1a..a86400a560 100644
--- a/tools/editor/file_type_cache.cpp
+++ b/tools/editor/file_type_cache.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/file_type_cache.h b/tools/editor/file_type_cache.h
index bb89718a55..efc6d22b25 100644
--- a/tools/editor/file_type_cache.h
+++ b/tools/editor/file_type_cache.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/fileserver/SCsub b/tools/editor/fileserver/SCsub
index b525fb3f75..363a2ce4c0 100644
--- a/tools/editor/fileserver/SCsub
+++ b/tools/editor/fileserver/SCsub
@@ -1,7 +1,3 @@
Import('env')
Export('env')
env.add_source_files(env.tool_sources,"*.cpp")
-
-
-
-
diff --git a/tools/editor/fileserver/editor_file_server.cpp b/tools/editor/fileserver/editor_file_server.cpp
index f21d9b4ec1..ea95e4da1c 100644
--- a/tools/editor/fileserver/editor_file_server.cpp
+++ b/tools/editor/fileserver/editor_file_server.cpp
@@ -278,6 +278,7 @@ void EditorFileServer::_thread_start(void*s) {
self->to_wait.erase(w);
self->wait_mutex->unlock();
Thread::wait_to_finish(w);
+ memdelete(w);
self->wait_mutex->lock();
}
self->wait_mutex->unlock();
@@ -317,27 +318,7 @@ EditorFileServer::EditorFileServer() {
cmd=CMD_NONE;
thread=Thread::create(_thread_start,this);
- List<IP_Address> local_ip;
- IP::get_singleton()->get_local_addresses(&local_ip);
EDITOR_DEF("file_server/port",6010);
- String lip;
- String hint;
- for(List<IP_Address>::Element *E=local_ip.front();E;E=E->next()) {
-
- String ip = E->get();
- if (ip=="127.0.0.1")
- continue;
-
- if (lip!="")
- lip=ip;
- if (hint!="")
- hint+=",";
- hint+=ip;
-
- }
-
- EDITOR_DEF("file_server/host",lip);
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"file_server/host",PROPERTY_HINT_ENUM,hint));
EDITOR_DEF("file_server/password","");
}
@@ -346,5 +327,6 @@ EditorFileServer::~EditorFileServer() {
quit=true;
Thread::wait_to_finish(thread);
+ memdelete(thread);
memdelete(wait_mutex);
}
diff --git a/tools/editor/groups_editor.cpp b/tools/editor/groups_editor.cpp
index 52db562d8a..2e82854014 100644
--- a/tools/editor/groups_editor.cpp
+++ b/tools/editor/groups_editor.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -39,6 +39,9 @@ void GroupsEditor::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_TREE) {
connect("confirmed", this,"_close");
}
+ if (p_what==NOTIFICATION_EXIT_TREE) {
+ disconnect("confirmed", this,"_close");
+ }
}
void GroupsEditor::_close() {
diff --git a/tools/editor/groups_editor.h b/tools/editor/groups_editor.h
index 6357358e1d..09883a150f 100644
--- a/tools/editor/groups_editor.h
+++ b/tools/editor/groups_editor.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/icons/SCsub b/tools/editor/icons/SCsub
index aea053d22b..addf6879a2 100644
--- a/tools/editor/icons/SCsub
+++ b/tools/editor/icons/SCsub
@@ -9,30 +9,30 @@ def make_editor_icons_action(target, source, env):
pixmaps = source
s = cStringIO.StringIO()
-
+
s.write("#include \"editor_icons.h\"\n\n")
s.write("#include \"scene/resources/theme.h\"\n\n")
for x in pixmaps:
-
+
x=str(x)
var_str=os.path.basename(x)[:-4]+"_png";
#print(var_str)
-
+
s.write("static const unsigned char "+ var_str +"[]={\n");
-
+
pngf=open(x,"rb");
-
+
b=pngf.read(1);
while(len(b)==1):
s.write(hex(ord(b)))
b=pngf.read(1);
if (len(b)==1):
s.write(",")
-
+
s.write("\n};\n\n\n");
pngf.close();
-
+
s.write("static Ref<ImageTexture> make_icon(const uint8_t* p_png) {\n")
s.write("\tRef<ImageTexture> texture( memnew( ImageTexture ) );\n")
s.write("\ttexture->create_from_image( Image(p_png),ImageTexture::FLAG_FILTER );\n")
@@ -42,14 +42,14 @@ def make_editor_icons_action(target, source, env):
s.write("void editor_register_icons(Ref<Theme> p_theme) {\n\n")
for x in pixmaps:
-
+
x=os.path.basename(str(x))
type=x[5:-4].title().replace("_","");
var_str=x[:-4]+"_png";
s.write("\tp_theme->set_icon(\""+type+"\",\"EditorIcons\",make_icon("+var_str+"));\n");
s.write("\n\n}\n\n");
-
+
f = open(dst,"wb")
f.write(s.getvalue())
f.close()
@@ -63,4 +63,3 @@ env.Alias('editor_icons',[env.MakeEditorIconsBuilder('#tools/editor/editor_icons
env.tool_sources.append("#tools/editor/editor_icons.cpp")
Export('env')
-
diff --git a/tools/editor/icons/icon_add.png b/tools/editor/icons/icon_add.png
index 1a97f356d6..26283ca67c 100644
--- a/tools/editor/icons/icon_add.png
+++ b/tools/editor/icons/icon_add.png
Binary files differ
diff --git a/tools/editor/icons/icon_anchor.png b/tools/editor/icons/icon_anchor.png
new file mode 100644
index 0000000000..1f9f9fa139
--- /dev/null
+++ b/tools/editor/icons/icon_anchor.png
Binary files differ
diff --git a/tools/editor/icons/icon_animation.png b/tools/editor/icons/icon_animation.png
index 6af126bf37..ac663c0554 100644
--- a/tools/editor/icons/icon_animation.png
+++ b/tools/editor/icons/icon_animation.png
Binary files differ
diff --git a/tools/editor/icons/icon_arrow_left.png b/tools/editor/icons/icon_arrow_left.png
new file mode 100644
index 0000000000..b72ac2a10d
--- /dev/null
+++ b/tools/editor/icons/icon_arrow_left.png
Binary files differ
diff --git a/tools/editor/icons/icon_arrow_left_disabled.png b/tools/editor/icons/icon_arrow_left_disabled.png
new file mode 100644
index 0000000000..f1f9d0f988
--- /dev/null
+++ b/tools/editor/icons/icon_arrow_left_disabled.png
Binary files differ
diff --git a/tools/editor/icons/icon_arrow_right.png b/tools/editor/icons/icon_arrow_right.png
new file mode 100644
index 0000000000..d0a48b01ac
--- /dev/null
+++ b/tools/editor/icons/icon_arrow_right.png
Binary files differ
diff --git a/tools/editor/icons/icon_arrow_right_disabled.png b/tools/editor/icons/icon_arrow_right_disabled.png
new file mode 100644
index 0000000000..840cd0da0a
--- /dev/null
+++ b/tools/editor/icons/icon_arrow_right_disabled.png
Binary files differ
diff --git a/tools/editor/icons/icon_arrow_up.png b/tools/editor/icons/icon_arrow_up.png
new file mode 100644
index 0000000000..10aac0e0f6
--- /dev/null
+++ b/tools/editor/icons/icon_arrow_up.png
Binary files differ
diff --git a/tools/editor/icons/icon_arrow_up_disabled.png b/tools/editor/icons/icon_arrow_up_disabled.png
new file mode 100644
index 0000000000..fb46aa1373
--- /dev/null
+++ b/tools/editor/icons/icon_arrow_up_disabled.png
Binary files differ
diff --git a/tools/editor/icons/icon_atlas_texture.png b/tools/editor/icons/icon_atlas_texture.png
index 3da9f0bee4..0051b0cda4 100644
--- a/tools/editor/icons/icon_atlas_texture.png
+++ b/tools/editor/icons/icon_atlas_texture.png
Binary files differ
diff --git a/tools/editor/icons/icon_audio_stream_gibberish.png b/tools/editor/icons/icon_audio_stream_gibberish.png
index f10671e8a2..95470c298e 100644
--- a/tools/editor/icons/icon_audio_stream_gibberish.png
+++ b/tools/editor/icons/icon_audio_stream_gibberish.png
Binary files differ
diff --git a/tools/editor/icons/icon_audio_stream_opus.png b/tools/editor/icons/icon_audio_stream_opus.png
new file mode 100644
index 0000000000..69b0c83b4d
--- /dev/null
+++ b/tools/editor/icons/icon_audio_stream_opus.png
Binary files differ
diff --git a/tools/editor/icons/icon_auto_play.png b/tools/editor/icons/icon_auto_play.png
index e454ca8c7b..1d5c957cc7 100644
--- a/tools/editor/icons/icon_auto_play.png
+++ b/tools/editor/icons/icon_auto_play.png
Binary files differ
diff --git a/tools/editor/icons/icon_back.png b/tools/editor/icons/icon_back.png
index d60e3b2640..f7e507d92b 100644
--- a/tools/editor/icons/icon_back.png
+++ b/tools/editor/icons/icon_back.png
Binary files differ
diff --git a/tools/editor/icons/icon_back_buffer_copy.png b/tools/editor/icons/icon_back_buffer_copy.png
new file mode 100644
index 0000000000..b27eb39108
--- /dev/null
+++ b/tools/editor/icons/icon_back_buffer_copy.png
Binary files differ
diff --git a/tools/editor/icons/icon_back_disabled.png b/tools/editor/icons/icon_back_disabled.png
new file mode 100644
index 0000000000..31aab496e2
--- /dev/null
+++ b/tools/editor/icons/icon_back_disabled.png
Binary files differ
diff --git a/tools/editor/icons/icon_bake.png b/tools/editor/icons/icon_bake.png
index b1b0f941da..ae06ce48e6 100644
--- a/tools/editor/icons/icon_bake.png
+++ b/tools/editor/icons/icon_bake.png
Binary files differ
diff --git a/tools/editor/icons/icon_blend.png b/tools/editor/icons/icon_blend.png
index 985b63d5d1..2a75f0b2f4 100644
--- a/tools/editor/icons/icon_blend.png
+++ b/tools/editor/icons/icon_blend.png
Binary files differ
diff --git a/tools/editor/icons/icon_bone.png b/tools/editor/icons/icon_bone.png
index 174b0bc167..81b6d8856e 100644
--- a/tools/editor/icons/icon_bone.png
+++ b/tools/editor/icons/icon_bone.png
Binary files differ
diff --git a/tools/editor/icons/icon_bool.png b/tools/editor/icons/icon_bool.png
index d465b7da07..3381033b00 100644
--- a/tools/editor/icons/icon_bool.png
+++ b/tools/editor/icons/icon_bool.png
Binary files differ
diff --git a/tools/editor/icons/icon_canvas_item.png b/tools/editor/icons/icon_canvas_item.png
index 99403bed21..add54ba1af 100644
--- a/tools/editor/icons/icon_canvas_item.png
+++ b/tools/editor/icons/icon_canvas_item.png
Binary files differ
diff --git a/tools/editor/icons/icon_canvas_item_material.png b/tools/editor/icons/icon_canvas_item_material.png
new file mode 100644
index 0000000000..2fe8921653
--- /dev/null
+++ b/tools/editor/icons/icon_canvas_item_material.png
Binary files differ
diff --git a/tools/editor/icons/icon_canvas_item_shader.png b/tools/editor/icons/icon_canvas_item_shader.png
new file mode 100644
index 0000000000..a5f4e7bf85
--- /dev/null
+++ b/tools/editor/icons/icon_canvas_item_shader.png
Binary files differ
diff --git a/tools/editor/icons/icon_canvas_item_shader_graph.png b/tools/editor/icons/icon_canvas_item_shader_graph.png
new file mode 100644
index 0000000000..bba966b43e
--- /dev/null
+++ b/tools/editor/icons/icon_canvas_item_shader_graph.png
Binary files differ
diff --git a/tools/editor/icons/icon_canvas_modulate.png b/tools/editor/icons/icon_canvas_modulate.png
new file mode 100644
index 0000000000..2a34df7793
--- /dev/null
+++ b/tools/editor/icons/icon_canvas_modulate.png
Binary files differ
diff --git a/tools/editor/icons/icon_check_box.png b/tools/editor/icons/icon_check_box.png
new file mode 100644
index 0000000000..8a2b56cc3e
--- /dev/null
+++ b/tools/editor/icons/icon_check_box.png
Binary files differ
diff --git a/tools/editor/icons/icon_class_list.png b/tools/editor/icons/icon_class_list.png
new file mode 100644
index 0000000000..fb756c0fe1
--- /dev/null
+++ b/tools/editor/icons/icon_class_list.png
Binary files differ
diff --git a/tools/editor/icons/icon_close.png b/tools/editor/icons/icon_close.png
index 11fa746271..10e56d5bb8 100644
--- a/tools/editor/icons/icon_close.png
+++ b/tools/editor/icons/icon_close.png
Binary files differ
diff --git a/tools/editor/icons/icon_close_hover.png b/tools/editor/icons/icon_close_hover.png
index efcc9e7471..cb519691e5 100644
--- a/tools/editor/icons/icon_close_hover.png
+++ b/tools/editor/icons/icon_close_hover.png
Binary files differ
diff --git a/tools/editor/icons/icon_collapse.png b/tools/editor/icons/icon_collapse.png
index bd5c9765a5..23db9e42a7 100644
--- a/tools/editor/icons/icon_collapse.png
+++ b/tools/editor/icons/icon_collapse.png
Binary files differ
diff --git a/tools/editor/icons/icon_collapse_hl.png b/tools/editor/icons/icon_collapse_hl.png
index 7ed9a5c125..0dfbc8b175 100644
--- a/tools/editor/icons/icon_collapse_hl.png
+++ b/tools/editor/icons/icon_collapse_hl.png
Binary files differ
diff --git a/tools/editor/icons/icon_color_ramp.png b/tools/editor/icons/icon_color_ramp.png
new file mode 100644
index 0000000000..9031b5ec53
--- /dev/null
+++ b/tools/editor/icons/icon_color_ramp.png
Binary files differ
diff --git a/tools/editor/icons/icon_connect.png b/tools/editor/icons/icon_connect.png
index 24258414c4..745e445a61 100644
--- a/tools/editor/icons/icon_connect.png
+++ b/tools/editor/icons/icon_connect.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_bottom_center.png b/tools/editor/icons/icon_control_align_bottom_center.png
new file mode 100644
index 0000000000..5ce9fe5c1c
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_bottom_center.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_bottom_left.png b/tools/editor/icons/icon_control_align_bottom_left.png
new file mode 100644
index 0000000000..6c5129bf95
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_bottom_left.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_bottom_right.png b/tools/editor/icons/icon_control_align_bottom_right.png
new file mode 100644
index 0000000000..8857f4e940
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_bottom_right.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_bottom_wide.png b/tools/editor/icons/icon_control_align_bottom_wide.png
new file mode 100644
index 0000000000..56f009b8e4
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_bottom_wide.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_center.png b/tools/editor/icons/icon_control_align_center.png
new file mode 100644
index 0000000000..acd42525fa
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_center.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_center_left.png b/tools/editor/icons/icon_control_align_center_left.png
new file mode 100644
index 0000000000..997074b097
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_center_left.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_center_right.png b/tools/editor/icons/icon_control_align_center_right.png
new file mode 100644
index 0000000000..b5cae63f7a
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_center_right.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_left_center.png b/tools/editor/icons/icon_control_align_left_center.png
new file mode 100644
index 0000000000..7bb4dfb567
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_left_center.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_left_wide.png b/tools/editor/icons/icon_control_align_left_wide.png
new file mode 100644
index 0000000000..1b0a6cff95
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_left_wide.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_right_center.png b/tools/editor/icons/icon_control_align_right_center.png
new file mode 100644
index 0000000000..cf12d44c6a
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_right_center.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_right_wide.png b/tools/editor/icons/icon_control_align_right_wide.png
new file mode 100644
index 0000000000..406ed25aed
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_right_wide.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_top_center.png b/tools/editor/icons/icon_control_align_top_center.png
new file mode 100644
index 0000000000..da7ede984a
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_top_center.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_top_left.png b/tools/editor/icons/icon_control_align_top_left.png
new file mode 100644
index 0000000000..84a224fbbe
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_top_left.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_top_right.png b/tools/editor/icons/icon_control_align_top_right.png
new file mode 100644
index 0000000000..3b58eead9c
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_top_right.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_top_wide.png b/tools/editor/icons/icon_control_align_top_wide.png
new file mode 100644
index 0000000000..869ae26134
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_top_wide.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_align_wide.png b/tools/editor/icons/icon_control_align_wide.png
new file mode 100644
index 0000000000..57a2933b25
--- /dev/null
+++ b/tools/editor/icons/icon_control_align_wide.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_hcenter_wide.png b/tools/editor/icons/icon_control_hcenter_wide.png
new file mode 100644
index 0000000000..739ea5baeb
--- /dev/null
+++ b/tools/editor/icons/icon_control_hcenter_wide.png
Binary files differ
diff --git a/tools/editor/icons/icon_control_vcenter_wide.png b/tools/editor/icons/icon_control_vcenter_wide.png
new file mode 100644
index 0000000000..44cbb8f344
--- /dev/null
+++ b/tools/editor/icons/icon_control_vcenter_wide.png
Binary files differ
diff --git a/tools/editor/icons/icon_curve_constant.png b/tools/editor/icons/icon_curve_constant.png
new file mode 100644
index 0000000000..cdeac1785e
--- /dev/null
+++ b/tools/editor/icons/icon_curve_constant.png
Binary files differ
diff --git a/tools/editor/icons/icon_curve_in.png b/tools/editor/icons/icon_curve_in.png
new file mode 100644
index 0000000000..2db202632e
--- /dev/null
+++ b/tools/editor/icons/icon_curve_in.png
Binary files differ
diff --git a/tools/editor/icons/icon_curve_in_out.png b/tools/editor/icons/icon_curve_in_out.png
new file mode 100644
index 0000000000..f4cb593496
--- /dev/null
+++ b/tools/editor/icons/icon_curve_in_out.png
Binary files differ
diff --git a/tools/editor/icons/icon_curve_linear.png b/tools/editor/icons/icon_curve_linear.png
new file mode 100644
index 0000000000..9467dc97a3
--- /dev/null
+++ b/tools/editor/icons/icon_curve_linear.png
Binary files differ
diff --git a/tools/editor/icons/icon_curve_out.png b/tools/editor/icons/icon_curve_out.png
new file mode 100644
index 0000000000..c91ade6f4a
--- /dev/null
+++ b/tools/editor/icons/icon_curve_out.png
Binary files differ
diff --git a/tools/editor/icons/icon_curve_out_in.png b/tools/editor/icons/icon_curve_out_in.png
new file mode 100644
index 0000000000..6499fa753e
--- /dev/null
+++ b/tools/editor/icons/icon_curve_out_in.png
Binary files differ
diff --git a/tools/editor/icons/icon_debug.png b/tools/editor/icons/icon_debug.png
new file mode 100644
index 0000000000..03b98aa9e4
--- /dev/null
+++ b/tools/editor/icons/icon_debug.png
Binary files differ
diff --git a/tools/editor/icons/icon_default_project_icon.png b/tools/editor/icons/icon_default_project_icon.png
index 3e991fcc29..e87a49bd28 100644
--- a/tools/editor/icons/icon_default_project_icon.png
+++ b/tools/editor/icons/icon_default_project_icon.png
Binary files differ
diff --git a/tools/editor/icons/icon_del.png b/tools/editor/icons/icon_del.png
index 5349af466e..10e56d5bb8 100644
--- a/tools/editor/icons/icon_del.png
+++ b/tools/editor/icons/icon_del.png
Binary files differ
diff --git a/tools/editor/icons/icon_duplicate.png b/tools/editor/icons/icon_duplicate.png
index bae4aa2c30..f854a14fd3 100644
--- a/tools/editor/icons/icon_duplicate.png
+++ b/tools/editor/icons/icon_duplicate.png
Binary files differ
diff --git a/tools/editor/icons/icon_edit.png b/tools/editor/icons/icon_edit.png
index 012a7f5f1d..157f785b83 100644
--- a/tools/editor/icons/icon_edit.png
+++ b/tools/editor/icons/icon_edit.png
Binary files differ
diff --git a/tools/editor/icons/icon_edit_key.png b/tools/editor/icons/icon_edit_key.png
index 43a7056f38..9ab1287fc6 100644
--- a/tools/editor/icons/icon_edit_key.png
+++ b/tools/editor/icons/icon_edit_key.png
Binary files differ
diff --git a/tools/editor/icons/icon_edit_resource.png b/tools/editor/icons/icon_edit_resource.png
index de538dfe95..31d0c68fc6 100644
--- a/tools/editor/icons/icon_edit_resource.png
+++ b/tools/editor/icons/icon_edit_resource.png
Binary files differ
diff --git a/tools/editor/icons/icon_editor_focus.png b/tools/editor/icons/icon_editor_focus.png
index f21d22ebd8..40ce11f381 100644
--- a/tools/editor/icons/icon_editor_focus.png
+++ b/tools/editor/icons/icon_editor_focus.png
Binary files differ
diff --git a/tools/editor/icons/icon_enum.png b/tools/editor/icons/icon_enum.png
index 2496e1d0db..ac36c96e28 100644
--- a/tools/editor/icons/icon_enum.png
+++ b/tools/editor/icons/icon_enum.png
Binary files differ
diff --git a/tools/editor/icons/icon_event_player.png b/tools/editor/icons/icon_event_player.png
index b67f91b1b3..68646b3dfe 100644
--- a/tools/editor/icons/icon_event_player.png
+++ b/tools/editor/icons/icon_event_player.png
Binary files differ
diff --git a/tools/editor/icons/icon_favorites.png b/tools/editor/icons/icon_favorites.png
index da4713d032..4a3b575f95 100644
--- a/tools/editor/icons/icon_favorites.png
+++ b/tools/editor/icons/icon_favorites.png
Binary files differ
diff --git a/tools/editor/icons/icon_file_big.png b/tools/editor/icons/icon_file_big.png
new file mode 100644
index 0000000000..887a4ee05c
--- /dev/null
+++ b/tools/editor/icons/icon_file_big.png
Binary files differ
diff --git a/tools/editor/icons/icon_file_list.png b/tools/editor/icons/icon_file_list.png
new file mode 100644
index 0000000000..ab790a85a5
--- /dev/null
+++ b/tools/editor/icons/icon_file_list.png
Binary files differ
diff --git a/tools/editor/icons/icon_file_server.png b/tools/editor/icons/icon_file_server.png
index 27c99127c3..4bd94fa8c8 100644
--- a/tools/editor/icons/icon_file_server.png
+++ b/tools/editor/icons/icon_file_server.png
Binary files differ
diff --git a/tools/editor/icons/icon_file_thumbnail.png b/tools/editor/icons/icon_file_thumbnail.png
new file mode 100644
index 0000000000..c32be5e8f8
--- /dev/null
+++ b/tools/editor/icons/icon_file_thumbnail.png
Binary files differ
diff --git a/tools/editor/icons/icon_filesystem.png b/tools/editor/icons/icon_filesystem.png
new file mode 100644
index 0000000000..6841f9a792
--- /dev/null
+++ b/tools/editor/icons/icon_filesystem.png
Binary files differ
diff --git a/tools/editor/icons/icon_folder.png b/tools/editor/icons/icon_folder.png
index 814f217edf..a450a7b297 100644
--- a/tools/editor/icons/icon_folder.png
+++ b/tools/editor/icons/icon_folder.png
Binary files differ
diff --git a/tools/editor/icons/icon_folder_big.png b/tools/editor/icons/icon_folder_big.png
new file mode 100644
index 0000000000..988e070f98
--- /dev/null
+++ b/tools/editor/icons/icon_folder_big.png
Binary files differ
diff --git a/tools/editor/icons/icon_font.png b/tools/editor/icons/icon_font.png
index d9554183c2..3ffe4f1b17 100644
--- a/tools/editor/icons/icon_font.png
+++ b/tools/editor/icons/icon_font.png
Binary files differ
diff --git a/tools/editor/icons/icon_forward.png b/tools/editor/icons/icon_forward.png
index ca6838ae9e..14e8bc9a5a 100644
--- a/tools/editor/icons/icon_forward.png
+++ b/tools/editor/icons/icon_forward.png
Binary files differ
diff --git a/tools/editor/icons/icon_g_d_script.png b/tools/editor/icons/icon_g_d_script.png
index 3b1cc98c4d..88d865356c 100644
--- a/tools/editor/icons/icon_g_d_script.png
+++ b/tools/editor/icons/icon_g_d_script.png
Binary files differ
diff --git a/tools/editor/icons/icon_godot.png b/tools/editor/icons/icon_godot.png
new file mode 100644
index 0000000000..e80820fc10
--- /dev/null
+++ b/tools/editor/icons/icon_godot.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_color_ramp.png b/tools/editor/icons/icon_graph_color_ramp.png
new file mode 100644
index 0000000000..9031b5ec53
--- /dev/null
+++ b/tools/editor/icons/icon_graph_color_ramp.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_comment.png b/tools/editor/icons/icon_graph_comment.png
new file mode 100644
index 0000000000..bf7889c510
--- /dev/null
+++ b/tools/editor/icons/icon_graph_comment.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_cube_uniform.png b/tools/editor/icons/icon_graph_cube_uniform.png
new file mode 100644
index 0000000000..d1b92b4943
--- /dev/null
+++ b/tools/editor/icons/icon_graph_cube_uniform.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_curve_map.png b/tools/editor/icons/icon_graph_curve_map.png
new file mode 100644
index 0000000000..de5c32f09e
--- /dev/null
+++ b/tools/editor/icons/icon_graph_curve_map.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_default_texture.png b/tools/editor/icons/icon_graph_default_texture.png
new file mode 100644
index 0000000000..da77ec9364
--- /dev/null
+++ b/tools/editor/icons/icon_graph_default_texture.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_input.png b/tools/editor/icons/icon_graph_input.png
new file mode 100644
index 0000000000..a396bc2350
--- /dev/null
+++ b/tools/editor/icons/icon_graph_input.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_rgb.png b/tools/editor/icons/icon_graph_rgb.png
new file mode 100644
index 0000000000..abffaedd34
--- /dev/null
+++ b/tools/editor/icons/icon_graph_rgb.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_rgb_op.png b/tools/editor/icons/icon_graph_rgb_op.png
new file mode 100644
index 0000000000..642fc838c2
--- /dev/null
+++ b/tools/editor/icons/icon_graph_rgb_op.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_rgb_uniform.png b/tools/editor/icons/icon_graph_rgb_uniform.png
new file mode 100644
index 0000000000..92c79997ef
--- /dev/null
+++ b/tools/editor/icons/icon_graph_rgb_uniform.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_scalar.png b/tools/editor/icons/icon_graph_scalar.png
new file mode 100644
index 0000000000..028d0e9ea4
--- /dev/null
+++ b/tools/editor/icons/icon_graph_scalar.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_scalar_interp.png b/tools/editor/icons/icon_graph_scalar_interp.png
new file mode 100644
index 0000000000..4f178a27c4
--- /dev/null
+++ b/tools/editor/icons/icon_graph_scalar_interp.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_scalar_op.png b/tools/editor/icons/icon_graph_scalar_op.png
new file mode 100644
index 0000000000..0fc4cae94c
--- /dev/null
+++ b/tools/editor/icons/icon_graph_scalar_op.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_scalar_uniform.png b/tools/editor/icons/icon_graph_scalar_uniform.png
new file mode 100644
index 0000000000..fc6590a8cf
--- /dev/null
+++ b/tools/editor/icons/icon_graph_scalar_uniform.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_scalars_to_vec.png b/tools/editor/icons/icon_graph_scalars_to_vec.png
new file mode 100644
index 0000000000..7ca39a2f56
--- /dev/null
+++ b/tools/editor/icons/icon_graph_scalars_to_vec.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_texscreen.png b/tools/editor/icons/icon_graph_texscreen.png
new file mode 100644
index 0000000000..e183a8fa56
--- /dev/null
+++ b/tools/editor/icons/icon_graph_texscreen.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_texture_uniform.png b/tools/editor/icons/icon_graph_texture_uniform.png
new file mode 100644
index 0000000000..7517ac1d92
--- /dev/null
+++ b/tools/editor/icons/icon_graph_texture_uniform.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_time.png b/tools/editor/icons/icon_graph_time.png
new file mode 100644
index 0000000000..b61e45589f
--- /dev/null
+++ b/tools/editor/icons/icon_graph_time.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_vec_dp.png b/tools/editor/icons/icon_graph_vec_dp.png
new file mode 100644
index 0000000000..059c3025e7
--- /dev/null
+++ b/tools/editor/icons/icon_graph_vec_dp.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_vec_interp.png b/tools/editor/icons/icon_graph_vec_interp.png
new file mode 100644
index 0000000000..daf7a00203
--- /dev/null
+++ b/tools/editor/icons/icon_graph_vec_interp.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_vec_length.png b/tools/editor/icons/icon_graph_vec_length.png
new file mode 100644
index 0000000000..60ade8c90a
--- /dev/null
+++ b/tools/editor/icons/icon_graph_vec_length.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_vec_op.png b/tools/editor/icons/icon_graph_vec_op.png
new file mode 100644
index 0000000000..f2a7a51123
--- /dev/null
+++ b/tools/editor/icons/icon_graph_vec_op.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_vec_scalar_op.png b/tools/editor/icons/icon_graph_vec_scalar_op.png
new file mode 100644
index 0000000000..f0f4e7a196
--- /dev/null
+++ b/tools/editor/icons/icon_graph_vec_scalar_op.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_vec_to_scalars.png b/tools/editor/icons/icon_graph_vec_to_scalars.png
new file mode 100644
index 0000000000..a677a7cc53
--- /dev/null
+++ b/tools/editor/icons/icon_graph_vec_to_scalars.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_vecs_to_xform.png b/tools/editor/icons/icon_graph_vecs_to_xform.png
new file mode 100644
index 0000000000..51216c93eb
--- /dev/null
+++ b/tools/editor/icons/icon_graph_vecs_to_xform.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_vector.png b/tools/editor/icons/icon_graph_vector.png
new file mode 100644
index 0000000000..9dfe47d757
--- /dev/null
+++ b/tools/editor/icons/icon_graph_vector.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_vector_uniform.png b/tools/editor/icons/icon_graph_vector_uniform.png
new file mode 100644
index 0000000000..611539fca7
--- /dev/null
+++ b/tools/editor/icons/icon_graph_vector_uniform.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_xform.png b/tools/editor/icons/icon_graph_xform.png
new file mode 100644
index 0000000000..22df472be4
--- /dev/null
+++ b/tools/editor/icons/icon_graph_xform.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_xform_mult.png b/tools/editor/icons/icon_graph_xform_mult.png
new file mode 100644
index 0000000000..5d0ce7982d
--- /dev/null
+++ b/tools/editor/icons/icon_graph_xform_mult.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_xform_scalar_func.png b/tools/editor/icons/icon_graph_xform_scalar_func.png
new file mode 100644
index 0000000000..e53f08a564
--- /dev/null
+++ b/tools/editor/icons/icon_graph_xform_scalar_func.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_xform_to_vecs.png b/tools/editor/icons/icon_graph_xform_to_vecs.png
new file mode 100644
index 0000000000..847261f726
--- /dev/null
+++ b/tools/editor/icons/icon_graph_xform_to_vecs.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_xform_uniform.png b/tools/editor/icons/icon_graph_xform_uniform.png
new file mode 100644
index 0000000000..94c9759b25
--- /dev/null
+++ b/tools/editor/icons/icon_graph_xform_uniform.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_xform_vec_func.png b/tools/editor/icons/icon_graph_xform_vec_func.png
new file mode 100644
index 0000000000..f3ba528896
--- /dev/null
+++ b/tools/editor/icons/icon_graph_xform_vec_func.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_xform_vec_imult.png b/tools/editor/icons/icon_graph_xform_vec_imult.png
new file mode 100644
index 0000000000..7e7330cb8c
--- /dev/null
+++ b/tools/editor/icons/icon_graph_xform_vec_imult.png
Binary files differ
diff --git a/tools/editor/icons/icon_graph_xform_vec_mult.png b/tools/editor/icons/icon_graph_xform_vec_mult.png
new file mode 100644
index 0000000000..f80a28c80d
--- /dev/null
+++ b/tools/editor/icons/icon_graph_xform_vec_mult.png
Binary files differ
diff --git a/tools/editor/icons/icon_grid.png b/tools/editor/icons/icon_grid.png
new file mode 100644
index 0000000000..dcdd86c9b5
--- /dev/null
+++ b/tools/editor/icons/icon_grid.png
Binary files differ
diff --git a/tools/editor/icons/icon_group.png b/tools/editor/icons/icon_group.png
index 577c84777e..d43b4958c9 100644
--- a/tools/editor/icons/icon_group.png
+++ b/tools/editor/icons/icon_group.png
Binary files differ
diff --git a/tools/editor/icons/icon_groups.png b/tools/editor/icons/icon_groups.png
index da4fd0d985..f4386821ed 100644
--- a/tools/editor/icons/icon_groups.png
+++ b/tools/editor/icons/icon_groups.png
Binary files differ
diff --git a/tools/editor/icons/icon_help.png b/tools/editor/icons/icon_help.png
index 3f4f8453a7..d2085589ae 100644
--- a/tools/editor/icons/icon_help.png
+++ b/tools/editor/icons/icon_help.png
Binary files differ
diff --git a/tools/editor/icons/icon_hidden.png b/tools/editor/icons/icon_hidden.png
index 45fcfc2f47..e51b9ad03a 100644
--- a/tools/editor/icons/icon_hidden.png
+++ b/tools/editor/icons/icon_hidden.png
Binary files differ
diff --git a/tools/editor/icons/icon_history.png b/tools/editor/icons/icon_history.png
new file mode 100644
index 0000000000..5c306c3ac9
--- /dev/null
+++ b/tools/editor/icons/icon_history.png
Binary files differ
diff --git a/tools/editor/icons/icon_image.png b/tools/editor/icons/icon_image.png
index a6b1fbf6c1..5919ca8c6d 100644
--- a/tools/editor/icons/icon_image.png
+++ b/tools/editor/icons/icon_image.png
Binary files differ
diff --git a/tools/editor/icons/icon_image_texture.png b/tools/editor/icons/icon_image_texture.png
index 4618d984b8..b87e284a52 100644
--- a/tools/editor/icons/icon_image_texture.png
+++ b/tools/editor/icons/icon_image_texture.png
Binary files differ
diff --git a/tools/editor/icons/icon_instance_options.png b/tools/editor/icons/icon_instance_options.png
index 2d3e98b2ea..9108448095 100644
--- a/tools/editor/icons/icon_instance_options.png
+++ b/tools/editor/icons/icon_instance_options.png
Binary files differ
diff --git a/tools/editor/icons/icon_integer.png b/tools/editor/icons/icon_integer.png
index 0e5b5abd62..32c8d9885b 100644
--- a/tools/editor/icons/icon_integer.png
+++ b/tools/editor/icons/icon_integer.png
Binary files differ
diff --git a/tools/editor/icons/icon_interp_cubic.png b/tools/editor/icons/icon_interp_cubic.png
index ab33aa7e6a..a946d70947 100644
--- a/tools/editor/icons/icon_interp_cubic.png
+++ b/tools/editor/icons/icon_interp_cubic.png
Binary files differ
diff --git a/tools/editor/icons/icon_interp_linear.png b/tools/editor/icons/icon_interp_linear.png
index bf3849ecaf..9174af39e7 100644
--- a/tools/editor/icons/icon_interp_linear.png
+++ b/tools/editor/icons/icon_interp_linear.png
Binary files differ
diff --git a/tools/editor/icons/icon_interp_raw.png b/tools/editor/icons/icon_interp_raw.png
index 48650d6e66..f12936493b 100644
--- a/tools/editor/icons/icon_interp_raw.png
+++ b/tools/editor/icons/icon_interp_raw.png
Binary files differ
diff --git a/tools/editor/icons/icon_item_list.png b/tools/editor/icons/icon_item_list.png
new file mode 100644
index 0000000000..f930e7ecaa
--- /dev/null
+++ b/tools/editor/icons/icon_item_list.png
Binary files differ
diff --git a/tools/editor/icons/icon_key.png b/tools/editor/icons/icon_key.png
index d647876866..d6096ef41f 100644
--- a/tools/editor/icons/icon_key.png
+++ b/tools/editor/icons/icon_key.png
Binary files differ
diff --git a/tools/editor/icons/icon_key_next.png b/tools/editor/icons/icon_key_next.png
new file mode 100644
index 0000000000..759008d064
--- /dev/null
+++ b/tools/editor/icons/icon_key_next.png
Binary files differ
diff --git a/tools/editor/icons/icon_key_selected.png b/tools/editor/icons/icon_key_selected.png
index d916c55286..562beef98a 100644
--- a/tools/editor/icons/icon_key_selected.png
+++ b/tools/editor/icons/icon_key_selected.png
Binary files differ
diff --git a/tools/editor/icons/icon_light_2d.png b/tools/editor/icons/icon_light_2d.png
new file mode 100644
index 0000000000..9162b33090
--- /dev/null
+++ b/tools/editor/icons/icon_light_2d.png
Binary files differ
diff --git a/tools/editor/icons/icon_light_map.png b/tools/editor/icons/icon_light_map.png
index 96d3f6e11c..e0333f06ea 100644
--- a/tools/editor/icons/icon_light_map.png
+++ b/tools/editor/icons/icon_light_map.png
Binary files differ
diff --git a/tools/editor/icons/icon_light_occluder_2d.png b/tools/editor/icons/icon_light_occluder_2d.png
new file mode 100644
index 0000000000..c66dd536d3
--- /dev/null
+++ b/tools/editor/icons/icon_light_occluder_2d.png
Binary files differ
diff --git a/tools/editor/icons/icon_live_debug.png b/tools/editor/icons/icon_live_debug.png
new file mode 100644
index 0000000000..ad55646b9a
--- /dev/null
+++ b/tools/editor/icons/icon_live_debug.png
Binary files differ
diff --git a/tools/editor/icons/icon_load.png b/tools/editor/icons/icon_load.png
index fdc06d38a3..a450a7b297 100644
--- a/tools/editor/icons/icon_load.png
+++ b/tools/editor/icons/icon_load.png
Binary files differ
diff --git a/tools/editor/icons/icon_lock.png b/tools/editor/icons/icon_lock.png
index 0cfd1d4ab1..995d87b6fb 100644
--- a/tools/editor/icons/icon_lock.png
+++ b/tools/editor/icons/icon_lock.png
Binary files differ
diff --git a/tools/editor/icons/icon_loop.png b/tools/editor/icons/icon_loop.png
index d75642359d..7bde451ca0 100644
--- a/tools/editor/icons/icon_loop.png
+++ b/tools/editor/icons/icon_loop.png
Binary files differ
diff --git a/tools/editor/icons/icon_main_play.png b/tools/editor/icons/icon_main_play.png
index 401708c49e..9e8cc6c4a9 100644
--- a/tools/editor/icons/icon_main_play.png
+++ b/tools/editor/icons/icon_main_play.png
Binary files differ
diff --git a/tools/editor/icons/icon_main_stop.png b/tools/editor/icons/icon_main_stop.png
index 3f54ba69c9..31a6cd601e 100644
--- a/tools/editor/icons/icon_main_stop.png
+++ b/tools/editor/icons/icon_main_stop.png
Binary files differ
diff --git a/tools/editor/icons/icon_material_shader.png b/tools/editor/icons/icon_material_shader.png
new file mode 100644
index 0000000000..0e476b2540
--- /dev/null
+++ b/tools/editor/icons/icon_material_shader.png
Binary files differ
diff --git a/tools/editor/icons/icon_material_shader_graph.png b/tools/editor/icons/icon_material_shader_graph.png
new file mode 100644
index 0000000000..68d8b4cb49
--- /dev/null
+++ b/tools/editor/icons/icon_material_shader_graph.png
Binary files differ
diff --git a/tools/editor/icons/icon_mirror_x.png b/tools/editor/icons/icon_mirror_x.png
index d20f90c1da..657e7f5458 100644
--- a/tools/editor/icons/icon_mirror_x.png
+++ b/tools/editor/icons/icon_mirror_x.png
Binary files differ
diff --git a/tools/editor/icons/icon_mirror_y.png b/tools/editor/icons/icon_mirror_y.png
index 5e2f710425..111aa5e4ae 100644
--- a/tools/editor/icons/icon_mirror_y.png
+++ b/tools/editor/icons/icon_mirror_y.png
Binary files differ
diff --git a/tools/editor/icons/icon_move_down.png b/tools/editor/icons/icon_move_down.png
index ef310e80e1..06c7246084 100644
--- a/tools/editor/icons/icon_move_down.png
+++ b/tools/editor/icons/icon_move_down.png
Binary files differ
diff --git a/tools/editor/icons/icon_move_down_hl.png b/tools/editor/icons/icon_move_down_hl.png
index dec56e8da8..f9de58a940 100644
--- a/tools/editor/icons/icon_move_down_hl.png
+++ b/tools/editor/icons/icon_move_down_hl.png
Binary files differ
diff --git a/tools/editor/icons/icon_move_up.png b/tools/editor/icons/icon_move_up.png
index d67b1aff0b..ca6c64f7a1 100644
--- a/tools/editor/icons/icon_move_up.png
+++ b/tools/editor/icons/icon_move_up.png
Binary files differ
diff --git a/tools/editor/icons/icon_move_up_hl.png b/tools/editor/icons/icon_move_up_hl.png
index 19ce8bbe27..e076c9a265 100644
--- a/tools/editor/icons/icon_move_up_hl.png
+++ b/tools/editor/icons/icon_move_up_hl.png
Binary files differ
diff --git a/tools/editor/icons/icon_multi_edit.png b/tools/editor/icons/icon_multi_edit.png
new file mode 100644
index 0000000000..70faee3d6a
--- /dev/null
+++ b/tools/editor/icons/icon_multi_edit.png
Binary files differ
diff --git a/tools/editor/icons/icon_multi_node_edit.png b/tools/editor/icons/icon_multi_node_edit.png
new file mode 100644
index 0000000000..357c062cbd
--- /dev/null
+++ b/tools/editor/icons/icon_multi_node_edit.png
Binary files differ
diff --git a/tools/editor/icons/icon_navigation_2d.png b/tools/editor/icons/icon_navigation_2d.png
new file mode 100644
index 0000000000..8170ecf68c
--- /dev/null
+++ b/tools/editor/icons/icon_navigation_2d.png
Binary files differ
diff --git a/tools/editor/icons/icon_navigation_polygon_instance.png b/tools/editor/icons/icon_navigation_polygon_instance.png
new file mode 100644
index 0000000000..9f9c318906
--- /dev/null
+++ b/tools/editor/icons/icon_navigation_polygon_instance.png
Binary files differ
diff --git a/tools/editor/icons/icon_new.png b/tools/editor/icons/icon_new.png
index 3596d2e8ea..c04785fc3f 100644
--- a/tools/editor/icons/icon_new.png
+++ b/tools/editor/icons/icon_new.png
Binary files differ
diff --git a/tools/editor/icons/icon_node.png b/tools/editor/icons/icon_node.png
index b0f7fb01dc..d8ce1b7538 100644
--- a/tools/editor/icons/icon_node.png
+++ b/tools/editor/icons/icon_node.png
Binary files differ
diff --git a/tools/editor/icons/icon_non_favorite.png b/tools/editor/icons/icon_non_favorite.png
new file mode 100644
index 0000000000..edd806fbe8
--- /dev/null
+++ b/tools/editor/icons/icon_non_favorite.png
Binary files differ
diff --git a/tools/editor/icons/icon_occluder_polygon_2d.png b/tools/editor/icons/icon_occluder_polygon_2d.png
new file mode 100644
index 0000000000..705794b729
--- /dev/null
+++ b/tools/editor/icons/icon_occluder_polygon_2d.png
Binary files differ
diff --git a/tools/editor/icons/icon_open.png b/tools/editor/icons/icon_open.png
index 4fad5677ca..a450a7b297 100644
--- a/tools/editor/icons/icon_open.png
+++ b/tools/editor/icons/icon_open.png
Binary files differ
diff --git a/tools/editor/icons/icon_p_hash_translation.png b/tools/editor/icons/icon_p_hash_translation.png
index c0eadc3c55..e18ef6a76f 100644
--- a/tools/editor/icons/icon_p_hash_translation.png
+++ b/tools/editor/icons/icon_p_hash_translation.png
Binary files differ
diff --git a/tools/editor/icons/icon_packed_scene.png b/tools/editor/icons/icon_packed_scene.png
index 9c1e1c4fbf..c9802f2b66 100644
--- a/tools/editor/icons/icon_packed_scene.png
+++ b/tools/editor/icons/icon_packed_scene.png
Binary files differ
diff --git a/tools/editor/icons/icon_panel_top.png b/tools/editor/icons/icon_panel_top.png
new file mode 100644
index 0000000000..20e67fad1a
--- /dev/null
+++ b/tools/editor/icons/icon_panel_top.png
Binary files differ
diff --git a/tools/editor/icons/icon_panels_1.png b/tools/editor/icons/icon_panels_1.png
index 501c8c9acc..546ca61c89 100644
--- a/tools/editor/icons/icon_panels_1.png
+++ b/tools/editor/icons/icon_panels_1.png
Binary files differ
diff --git a/tools/editor/icons/icon_panels_2.png b/tools/editor/icons/icon_panels_2.png
index 08f104e2b1..5a4750bda2 100644
--- a/tools/editor/icons/icon_panels_2.png
+++ b/tools/editor/icons/icon_panels_2.png
Binary files differ
diff --git a/tools/editor/icons/icon_panels_3.png b/tools/editor/icons/icon_panels_3.png
index 1d1902d8dd..13988de93a 100644
--- a/tools/editor/icons/icon_panels_3.png
+++ b/tools/editor/icons/icon_panels_3.png
Binary files differ
diff --git a/tools/editor/icons/icon_panels_4.png b/tools/editor/icons/icon_panels_4.png
index 83cc133d21..c217330d43 100644
--- a/tools/editor/icons/icon_panels_4.png
+++ b/tools/editor/icons/icon_panels_4.png
Binary files differ
diff --git a/tools/editor/icons/icon_patch_9_frame.png b/tools/editor/icons/icon_patch_9_frame.png
new file mode 100644
index 0000000000..c8f38fa61a
--- /dev/null
+++ b/tools/editor/icons/icon_patch_9_frame.png
Binary files differ
diff --git a/tools/editor/icons/icon_pause.png b/tools/editor/icons/icon_pause.png
index e63661353c..7c0a57003e 100644
--- a/tools/editor/icons/icon_pause.png
+++ b/tools/editor/icons/icon_pause.png
Binary files differ
diff --git a/tools/editor/icons/icon_pin.png b/tools/editor/icons/icon_pin.png
index f34c8585f7..037352137d 100644
--- a/tools/editor/icons/icon_pin.png
+++ b/tools/editor/icons/icon_pin.png
Binary files differ
diff --git a/tools/editor/icons/icon_pin_pressed.png b/tools/editor/icons/icon_pin_pressed.png
index f151b5a590..5738e6856f 100644
--- a/tools/editor/icons/icon_pin_pressed.png
+++ b/tools/editor/icons/icon_pin_pressed.png
Binary files differ
diff --git a/tools/editor/icons/icon_play.png b/tools/editor/icons/icon_play.png
index 544b3bc5f4..d2bd9d310c 100644
--- a/tools/editor/icons/icon_play.png
+++ b/tools/editor/icons/icon_play.png
Binary files differ
diff --git a/tools/editor/icons/icon_play_backwards.png b/tools/editor/icons/icon_play_backwards.png
new file mode 100644
index 0000000000..8dff5f20e3
--- /dev/null
+++ b/tools/editor/icons/icon_play_backwards.png
Binary files differ
diff --git a/tools/editor/icons/icon_play_custom.png b/tools/editor/icons/icon_play_custom.png
index 5c98c7100b..8e8ab8c62a 100644
--- a/tools/editor/icons/icon_play_custom.png
+++ b/tools/editor/icons/icon_play_custom.png
Binary files differ
diff --git a/tools/editor/icons/icon_play_scene.png b/tools/editor/icons/icon_play_scene.png
index 7ca59fe900..7079cc9677 100644
--- a/tools/editor/icons/icon_play_scene.png
+++ b/tools/editor/icons/icon_play_scene.png
Binary files differ
diff --git a/tools/editor/icons/icon_play_start.png b/tools/editor/icons/icon_play_start.png
new file mode 100644
index 0000000000..5b085aa1ed
--- /dev/null
+++ b/tools/editor/icons/icon_play_start.png
Binary files differ
diff --git a/tools/editor/icons/icon_play_start_backwards.png b/tools/editor/icons/icon_play_start_backwards.png
new file mode 100644
index 0000000000..09afac637c
--- /dev/null
+++ b/tools/editor/icons/icon_play_start_backwards.png
Binary files differ
diff --git a/tools/editor/icons/icon_prev_scene.png b/tools/editor/icons/icon_prev_scene.png
index c7c180e1c4..9d8dda5180 100644
--- a/tools/editor/icons/icon_prev_scene.png
+++ b/tools/editor/icons/icon_prev_scene.png
Binary files differ
diff --git a/tools/editor/icons/icon_real.png b/tools/editor/icons/icon_real.png
index bfe5038319..80fbf7017c 100644
--- a/tools/editor/icons/icon_real.png
+++ b/tools/editor/icons/icon_real.png
Binary files differ
diff --git a/tools/editor/icons/icon_region_edit.png b/tools/editor/icons/icon_region_edit.png
new file mode 100644
index 0000000000..824607f2cc
--- /dev/null
+++ b/tools/editor/icons/icon_region_edit.png
Binary files differ
diff --git a/tools/editor/icons/icon_reload.png b/tools/editor/icons/icon_reload.png
index 07f53efb56..f7c6530d77 100644
--- a/tools/editor/icons/icon_reload.png
+++ b/tools/editor/icons/icon_reload.png
Binary files differ
diff --git a/tools/editor/icons/icon_remote.png b/tools/editor/icons/icon_remote.png
new file mode 100644
index 0000000000..792d958a46
--- /dev/null
+++ b/tools/editor/icons/icon_remote.png
Binary files differ
diff --git a/tools/editor/icons/icon_remove.png b/tools/editor/icons/icon_remove.png
index 5349af466e..f0f814e304 100644
--- a/tools/editor/icons/icon_remove.png
+++ b/tools/editor/icons/icon_remove.png
Binary files differ
diff --git a/tools/editor/icons/icon_remove_small.png b/tools/editor/icons/icon_remove_small.png
new file mode 100644
index 0000000000..e0903689cf
--- /dev/null
+++ b/tools/editor/icons/icon_remove_small.png
Binary files differ
diff --git a/tools/editor/icons/icon_rename.png b/tools/editor/icons/icon_rename.png
index f88da39915..7b6a10df93 100644
--- a/tools/editor/icons/icon_rename.png
+++ b/tools/editor/icons/icon_rename.png
Binary files differ
diff --git a/tools/editor/icons/icon_reparent.png b/tools/editor/icons/icon_reparent.png
index af85b17ecc..59aee5e42d 100644
--- a/tools/editor/icons/icon_reparent.png
+++ b/tools/editor/icons/icon_reparent.png
Binary files differ
diff --git a/tools/editor/icons/icon_replace.png b/tools/editor/icons/icon_replace.png
index 2ae843ae10..662a58dc93 100644
--- a/tools/editor/icons/icon_replace.png
+++ b/tools/editor/icons/icon_replace.png
Binary files differ
diff --git a/tools/editor/icons/icon_resource_preloader.png b/tools/editor/icons/icon_resource_preloader.png
index e31e5a0d59..14b8c4de3c 100644
--- a/tools/editor/icons/icon_resource_preloader.png
+++ b/tools/editor/icons/icon_resource_preloader.png
Binary files differ
diff --git a/tools/editor/icons/icon_rotate_0.png b/tools/editor/icons/icon_rotate_0.png
new file mode 100644
index 0000000000..85a4b4c420
--- /dev/null
+++ b/tools/editor/icons/icon_rotate_0.png
Binary files differ
diff --git a/tools/editor/icons/icon_rotate_180.png b/tools/editor/icons/icon_rotate_180.png
new file mode 100644
index 0000000000..c4c516cff5
--- /dev/null
+++ b/tools/editor/icons/icon_rotate_180.png
Binary files differ
diff --git a/tools/editor/icons/icon_rotate_270.png b/tools/editor/icons/icon_rotate_270.png
new file mode 100644
index 0000000000..6e0f2e62b8
--- /dev/null
+++ b/tools/editor/icons/icon_rotate_270.png
Binary files differ
diff --git a/tools/editor/icons/icon_rotate_90.png b/tools/editor/icons/icon_rotate_90.png
new file mode 100644
index 0000000000..f25b0e99a3
--- /dev/null
+++ b/tools/editor/icons/icon_rotate_90.png
Binary files differ
diff --git a/tools/editor/icons/icon_sample_player.png b/tools/editor/icons/icon_sample_player.png
index 5561769b05..92d9cc77bf 100644
--- a/tools/editor/icons/icon_sample_player.png
+++ b/tools/editor/icons/icon_sample_player.png
Binary files differ
diff --git a/tools/editor/icons/icon_save.png b/tools/editor/icons/icon_save.png
index dce274ffb1..ddef66688d 100644
--- a/tools/editor/icons/icon_save.png
+++ b/tools/editor/icons/icon_save.png
Binary files differ
diff --git a/tools/editor/icons/icon_script.png b/tools/editor/icons/icon_script.png
index 65fb3c4934..baf5927c18 100644
--- a/tools/editor/icons/icon_script.png
+++ b/tools/editor/icons/icon_script.png
Binary files differ
diff --git a/tools/editor/icons/icon_script_list.png b/tools/editor/icons/icon_script_list.png
new file mode 100644
index 0000000000..cdea1e161e
--- /dev/null
+++ b/tools/editor/icons/icon_script_list.png
Binary files differ
diff --git a/tools/editor/icons/icon_sound_room_params.png b/tools/editor/icons/icon_sound_room_params.png
index 8e381d7978..2d37a4b49f 100644
--- a/tools/editor/icons/icon_sound_room_params.png
+++ b/tools/editor/icons/icon_sound_room_params.png
Binary files differ
diff --git a/tools/editor/icons/icon_stop.png b/tools/editor/icons/icon_stop.png
index 3b7562fa4a..0fd43b403a 100644
--- a/tools/editor/icons/icon_stop.png
+++ b/tools/editor/icons/icon_stop.png
Binary files differ
diff --git a/tools/editor/icons/icon_stream_player.png b/tools/editor/icons/icon_stream_player.png
index 2670a567e8..cf8fdcbaea 100644
--- a/tools/editor/icons/icon_stream_player.png
+++ b/tools/editor/icons/icon_stream_player.png
Binary files differ
diff --git a/tools/editor/icons/icon_string.png b/tools/editor/icons/icon_string.png
index 86cc8e633f..48bf753c40 100644
--- a/tools/editor/icons/icon_string.png
+++ b/tools/editor/icons/icon_string.png
Binary files differ
diff --git a/tools/editor/icons/icon_tab_menu.png b/tools/editor/icons/icon_tab_menu.png
new file mode 100644
index 0000000000..29edd02f01
--- /dev/null
+++ b/tools/editor/icons/icon_tab_menu.png
Binary files differ
diff --git a/tools/editor/icons/icon_texture.png b/tools/editor/icons/icon_texture.png
index 03d6ac7db2..bbcc54bd6e 100644
--- a/tools/editor/icons/icon_texture.png
+++ b/tools/editor/icons/icon_texture.png
Binary files differ
diff --git a/tools/editor/icons/icon_timer.png b/tools/editor/icons/icon_timer.png
index 3855683033..e8c36ae893 100644
--- a/tools/editor/icons/icon_timer.png
+++ b/tools/editor/icons/icon_timer.png
Binary files differ
diff --git a/tools/editor/icons/icon_tool_move.png b/tools/editor/icons/icon_tool_move.png
index fc611cdbb1..7257d3897b 100644
--- a/tools/editor/icons/icon_tool_move.png
+++ b/tools/editor/icons/icon_tool_move.png
Binary files differ
diff --git a/tools/editor/icons/icon_tool_pan.png b/tools/editor/icons/icon_tool_pan.png
index 5c078a7b1c..bfe6fddf45 100644
--- a/tools/editor/icons/icon_tool_pan.png
+++ b/tools/editor/icons/icon_tool_pan.png
Binary files differ
diff --git a/tools/editor/icons/icon_tool_rotate.png b/tools/editor/icons/icon_tool_rotate.png
index c833b93d6e..9575ceb54e 100644
--- a/tools/editor/icons/icon_tool_rotate.png
+++ b/tools/editor/icons/icon_tool_rotate.png
Binary files differ
diff --git a/tools/editor/icons/icon_tool_scale.png b/tools/editor/icons/icon_tool_scale.png
index 3eaeae1e99..a94a6e7c98 100644
--- a/tools/editor/icons/icon_tool_scale.png
+++ b/tools/editor/icons/icon_tool_scale.png
Binary files differ
diff --git a/tools/editor/icons/icon_tool_select.png b/tools/editor/icons/icon_tool_select.png
index eb5ff6e1da..47683228e9 100644
--- a/tools/editor/icons/icon_tool_select.png
+++ b/tools/editor/icons/icon_tool_select.png
Binary files differ
diff --git a/tools/editor/icons/icon_tools.png b/tools/editor/icons/icon_tools.png
index 927173ea0f..f02d924203 100644
--- a/tools/editor/icons/icon_tools.png
+++ b/tools/editor/icons/icon_tools.png
Binary files differ
diff --git a/tools/editor/icons/icon_track_continuous.png b/tools/editor/icons/icon_track_continuous.png
index 97e8762299..9f99891c21 100644
--- a/tools/editor/icons/icon_track_continuous.png
+++ b/tools/editor/icons/icon_track_continuous.png
Binary files differ
diff --git a/tools/editor/icons/icon_track_discrete.png b/tools/editor/icons/icon_track_discrete.png
index 57a4cd5579..4e65e49afb 100644
--- a/tools/editor/icons/icon_track_discrete.png
+++ b/tools/editor/icons/icon_track_discrete.png
Binary files differ
diff --git a/tools/editor/icons/icon_translation.png b/tools/editor/icons/icon_translation.png
index 6211ab9a1b..917c6f548a 100644
--- a/tools/editor/icons/icon_translation.png
+++ b/tools/editor/icons/icon_translation.png
Binary files differ
diff --git a/tools/editor/icons/icon_transpose.png b/tools/editor/icons/icon_transpose.png
new file mode 100644
index 0000000000..f9b78bc0fd
--- /dev/null
+++ b/tools/editor/icons/icon_transpose.png
Binary files differ
diff --git a/tools/editor/icons/icon_unbone.png b/tools/editor/icons/icon_unbone.png
index 819e8a8e5d..c8cd774460 100644
--- a/tools/editor/icons/icon_unbone.png
+++ b/tools/editor/icons/icon_unbone.png
Binary files differ
diff --git a/tools/editor/icons/icon_ungroup.png b/tools/editor/icons/icon_ungroup.png
index 16511e3f1c..4ea620bf96 100644
--- a/tools/editor/icons/icon_ungroup.png
+++ b/tools/editor/icons/icon_ungroup.png
Binary files differ
diff --git a/tools/editor/icons/icon_unlock.png b/tools/editor/icons/icon_unlock.png
index b86447bf7a..f9fa31c3e0 100644
--- a/tools/editor/icons/icon_unlock.png
+++ b/tools/editor/icons/icon_unlock.png
Binary files differ
diff --git a/tools/editor/icons/icon_uv.png b/tools/editor/icons/icon_uv.png
index 4d9d198d86..39bc737a37 100644
--- a/tools/editor/icons/icon_uv.png
+++ b/tools/editor/icons/icon_uv.png
Binary files differ
diff --git a/tools/editor/icons/icon_vector.png b/tools/editor/icons/icon_vector.png
index 7826d7f7a9..0ee33ba0b7 100644
--- a/tools/editor/icons/icon_vector.png
+++ b/tools/editor/icons/icon_vector.png
Binary files differ
diff --git a/tools/editor/icons/icon_vector2.png b/tools/editor/icons/icon_vector2.png
index 44e48c36c7..5920109a55 100644
--- a/tools/editor/icons/icon_vector2.png
+++ b/tools/editor/icons/icon_vector2.png
Binary files differ
diff --git a/tools/editor/icons/icon_viewport.png b/tools/editor/icons/icon_viewport.png
index 0a0d93cf4d..3859f6c7e9 100644
--- a/tools/editor/icons/icon_viewport.png
+++ b/tools/editor/icons/icon_viewport.png
Binary files differ
diff --git a/tools/editor/icons/icon_visible.png b/tools/editor/icons/icon_visible.png
index 519898bbef..cbc44c4e30 100644
--- a/tools/editor/icons/icon_visible.png
+++ b/tools/editor/icons/icon_visible.png
Binary files differ
diff --git a/tools/editor/icons/icon_wait_no_preview.png b/tools/editor/icons/icon_wait_no_preview.png
new file mode 100644
index 0000000000..5d20cd99ec
--- /dev/null
+++ b/tools/editor/icons/icon_wait_no_preview.png
Binary files differ
diff --git a/tools/editor/icons/icon_wait_preview_1.png b/tools/editor/icons/icon_wait_preview_1.png
new file mode 100644
index 0000000000..0aab42e04a
--- /dev/null
+++ b/tools/editor/icons/icon_wait_preview_1.png
Binary files differ
diff --git a/tools/editor/icons/icon_wait_preview_2.png b/tools/editor/icons/icon_wait_preview_2.png
new file mode 100644
index 0000000000..f476b9ce1f
--- /dev/null
+++ b/tools/editor/icons/icon_wait_preview_2.png
Binary files differ
diff --git a/tools/editor/icons/icon_wait_preview_3.png b/tools/editor/icons/icon_wait_preview_3.png
new file mode 100644
index 0000000000..2775d1ef43
--- /dev/null
+++ b/tools/editor/icons/icon_wait_preview_3.png
Binary files differ
diff --git a/tools/editor/icons/icon_wait_preview_4.png b/tools/editor/icons/icon_wait_preview_4.png
new file mode 100644
index 0000000000..2eaa86fec9
--- /dev/null
+++ b/tools/editor/icons/icon_wait_preview_4.png
Binary files differ
diff --git a/tools/editor/icons/icon_wait_preview_5.png b/tools/editor/icons/icon_wait_preview_5.png
new file mode 100644
index 0000000000..6590644bc1
--- /dev/null
+++ b/tools/editor/icons/icon_wait_preview_5.png
Binary files differ
diff --git a/tools/editor/icons/icon_wait_preview_6.png b/tools/editor/icons/icon_wait_preview_6.png
new file mode 100644
index 0000000000..307e412310
--- /dev/null
+++ b/tools/editor/icons/icon_wait_preview_6.png
Binary files differ
diff --git a/tools/editor/icons/icon_wait_preview_7.png b/tools/editor/icons/icon_wait_preview_7.png
new file mode 100644
index 0000000000..b0edc94d93
--- /dev/null
+++ b/tools/editor/icons/icon_wait_preview_7.png
Binary files differ
diff --git a/tools/editor/icons/icon_wait_preview_8.png b/tools/editor/icons/icon_wait_preview_8.png
new file mode 100644
index 0000000000..67a2f48ec3
--- /dev/null
+++ b/tools/editor/icons/icon_wait_preview_8.png
Binary files differ
diff --git a/tools/editor/icons/icon_zoom.png b/tools/editor/icons/icon_zoom.png
index cbacaaaeca..e4bbbfe7c3 100644
--- a/tools/editor/icons/icon_zoom.png
+++ b/tools/editor/icons/icon_zoom.png
Binary files differ
diff --git a/tools/editor/import_settings.cpp b/tools/editor/import_settings.cpp
index 63b8d65b69..36d7828be0 100644
--- a/tools/editor/import_settings.cpp
+++ b/tools/editor/import_settings.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/import_settings.h b/tools/editor/import_settings.h
index eb7a8b6143..31237dd8cf 100644
--- a/tools/editor/import_settings.h
+++ b/tools/editor/import_settings.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/inspector_dock.cpp b/tools/editor/inspector_dock.cpp
new file mode 100644
index 0000000000..57d19c3ec8
--- /dev/null
+++ b/tools/editor/inspector_dock.cpp
@@ -0,0 +1,22 @@
+#include "inspector_dock.h"
+
+#if 0
+void InspectorDock::_go_next() {
+
+
+}
+
+void InspectorDock::_go_prev() {
+
+
+}
+
+void InspectorDock::_bind_methods() {
+
+}
+
+InspectorDock::InspectorDock() {
+
+
+}
+#endif
diff --git a/tools/editor/inspector_dock.h b/tools/editor/inspector_dock.h
new file mode 100644
index 0000000000..90f043aba8
--- /dev/null
+++ b/tools/editor/inspector_dock.h
@@ -0,0 +1,35 @@
+#ifndef INSPECTOR_DOCK_H
+#define INSPECTOR_DOCK_H
+
+#include "scene/gui/box_container.h"
+#include "property_editor.h"
+
+
+//this is for now bundled in EditorNode, will be moved away here eventually
+
+#if 0
+class InspectorDock : public VBoxContainer
+{
+ OBJ_TYPE(InspectorDock,VBoxContainer);
+
+ PropertyEditor *property_editor;
+
+ EditorHistory editor_history;
+
+ void _go_next();
+ void _go_prev();
+
+protected:
+
+ static void _bind_methods();
+public:
+
+ EditorHistory &get_editor_history();
+
+ PropertyEditor *get_property_editor();
+
+ InspectorDock();
+};
+
+#endif
+#endif // INSPECTOR_DOCK_H
diff --git a/tools/editor/io_plugins/SCsub b/tools/editor/io_plugins/SCsub
index b525fb3f75..363a2ce4c0 100644
--- a/tools/editor/io_plugins/SCsub
+++ b/tools/editor/io_plugins/SCsub
@@ -1,7 +1,3 @@
Import('env')
Export('env')
env.add_source_files(env.tool_sources,"*.cpp")
-
-
-
-
diff --git a/tools/editor/io_plugins/editor_atlas.cpp b/tools/editor/io_plugins/editor_atlas.cpp
index 4c716874ba..7e9acd193d 100644
--- a/tools/editor/io_plugins/editor_atlas.cpp
+++ b/tools/editor/io_plugins/editor_atlas.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,7 +27,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "editor_atlas.h"
-
+#include "print_string.h"
struct _EditorAtlasWorkRect {
@@ -83,6 +83,7 @@ void EditorAtlas::fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, S
//place them
int ofs=0;
int limit_h=0;
+
for(int j=0;j<wrects.size();j++) {
@@ -100,6 +101,9 @@ void EditorAtlas::fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, S
wrects[j].p.x=ofs;
wrects[j].p.y=from_y;
+
+
+
int end_h = from_y+wrects[j].s.height;
int end_w = ofs+wrects[j].s.width;
if (ofs==0)
@@ -116,7 +120,7 @@ void EditorAtlas::fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, S
if (end_w > max_w)
max_w=end_w;
- if (ofs==0 || end_h>limit_h ) //while h limit not reched, keep stacking
+ //if (ofs==0 || end_h>limit_h ) //while h limit not reched, keep stacking
ofs+=wrects[j].s.width;
}
@@ -136,8 +140,8 @@ void EditorAtlas::fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, S
for(int i=0;i<results.size();i++) {
- float h = nearest_power_of_2(results[i].max_h);
- float w = nearest_power_of_2(results[i].max_w);
+ float h = results[i].max_h;
+ float w = results[i].max_w;
float aspect = h>w ? h/w : w/h;
if (aspect < best_aspect) {
best=i;
diff --git a/tools/editor/io_plugins/editor_atlas.h b/tools/editor/io_plugins/editor_atlas.h
index 685cf60c9d..716faff0c6 100644
--- a/tools/editor/io_plugins/editor_atlas.h
+++ b/tools/editor/io_plugins/editor_atlas.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/io_plugins/editor_font_import_plugin.cpp b/tools/editor/io_plugins/editor_font_import_plugin.cpp
index 064758f6cd..10a3877529 100644
--- a/tools/editor/io_plugins/editor_font_import_plugin.cpp
+++ b/tools/editor/io_plugins/editor_font_import_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,7 +28,7 @@
/*************************************************************************/
#include "editor_font_import_plugin.h"
#include "scene/gui/dialogs.h"
-#include "scene/gui/file_dialog.h"
+#include "tools/editor/editor_file_dialog.h"
#include "tools/editor/editor_node.h"
#include "os/file_access.h"
#include "editor_atlas.h"
@@ -47,6 +47,12 @@ class _EditorFontImportOptions : public Object {
OBJ_TYPE(_EditorFontImportOptions,Object);
public:
+ enum FontMode {
+
+ FONT_BITMAP,
+ FONT_DISTANCE_FIELD
+ };
+
enum ColorType {
COLOR_WHITE,
COLOR_CUSTOM,
@@ -69,6 +75,9 @@ public:
CHARSET_CUSTOM_LATIN
};
+
+ FontMode font_mode;
+
CharacterSet character_set;
String custom_file;
@@ -91,7 +100,6 @@ public:
bool color_use_monochrome;
String gradient_image;
-
bool disable_filter;
bool round_advance;
@@ -100,7 +108,10 @@ public:
bool _set(const StringName& p_name, const Variant& p_value) {
String n = p_name;
- if (n=="extra_space/char")
+ if (n=="mode/mode") {
+ font_mode=FontMode(int(p_value));
+ _change_notify();
+ } else if (n=="extra_space/char")
char_extra_spacing=p_value;
else if (n=="extra_space/space")
space_extra_spacing=p_value;
@@ -169,7 +180,9 @@ public:
bool _get(const StringName& p_name,Variant &r_ret) const{
String n = p_name;
- if (n=="extra_space/char")
+ if (n=="mode/mode")
+ r_ret=font_mode;
+ else if (n=="extra_space/char")
r_ret=char_extra_spacing;
else if (n=="extra_space/space")
r_ret=space_extra_spacing;
@@ -231,6 +244,9 @@ public:
void _get_property_list( List<PropertyInfo> *p_list) const{
+
+ p_list->push_back(PropertyInfo(Variant::INT,"mode/mode",PROPERTY_HINT_ENUM,"Bitmap,Distance Field"));
+
p_list->push_back(PropertyInfo(Variant::INT,"extra_space/char",PROPERTY_HINT_RANGE,"-64,64,1"));
p_list->push_back(PropertyInfo(Variant::INT,"extra_space/space",PROPERTY_HINT_RANGE,"-64,64,1"));
p_list->push_back(PropertyInfo(Variant::INT,"extra_space/top",PROPERTY_HINT_RANGE,"-64,64,1"));
@@ -240,35 +256,45 @@ public:
if (character_set>=CHARSET_CUSTOM)
p_list->push_back(PropertyInfo(Variant::STRING,"character_set/custom",PROPERTY_HINT_FILE));
- p_list->push_back(PropertyInfo(Variant::BOOL,"shadow/enabled"));
- if (shadow) {
- p_list->push_back(PropertyInfo(Variant::INT,"shadow/radius",PROPERTY_HINT_RANGE,"-64,64,1"));
- p_list->push_back(PropertyInfo(Variant::VECTOR2,"shadow/offset"));
- p_list->push_back(PropertyInfo(Variant::COLOR,"shadow/color"));
- p_list->push_back(PropertyInfo(Variant::REAL,"shadow/transition",PROPERTY_HINT_EXP_EASING));
- }
+ int usage = PROPERTY_USAGE_DEFAULT;
- p_list->push_back(PropertyInfo(Variant::BOOL,"shadow2/enabled"));
- if (shadow2) {
- p_list->push_back(PropertyInfo(Variant::INT,"shadow2/radius",PROPERTY_HINT_RANGE,"-64,64,1"));
- p_list->push_back(PropertyInfo(Variant::VECTOR2,"shadow2/offset"));
- p_list->push_back(PropertyInfo(Variant::COLOR,"shadow2/color"));
- p_list->push_back(PropertyInfo(Variant::REAL,"shadow2/transition",PROPERTY_HINT_EXP_EASING));
+ if (font_mode==FONT_DISTANCE_FIELD) {
+ usage = PROPERTY_USAGE_NOEDITOR;
}
- p_list->push_back(PropertyInfo(Variant::INT,"color/mode",PROPERTY_HINT_ENUM,"White,Color,Gradient,Gradient Image"));
- if (color_type==COLOR_CUSTOM) {
- p_list->push_back(PropertyInfo(Variant::COLOR,"color/color"));
+ {
+ p_list->push_back(PropertyInfo(Variant::BOOL,"shadow/enabled",PROPERTY_HINT_NONE,"",usage));
+ if (shadow) {
+ p_list->push_back(PropertyInfo(Variant::INT,"shadow/radius",PROPERTY_HINT_RANGE,"-64,64,1",usage));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2,"shadow/offset",PROPERTY_HINT_NONE,"",usage));
+ p_list->push_back(PropertyInfo(Variant::COLOR,"shadow/color",PROPERTY_HINT_NONE,"",usage));
+ p_list->push_back(PropertyInfo(Variant::REAL,"shadow/transition",PROPERTY_HINT_EXP_EASING,"",usage));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::BOOL,"shadow2/enabled",PROPERTY_HINT_NONE,"",usage));
+ if (shadow2) {
+ p_list->push_back(PropertyInfo(Variant::INT,"shadow2/radius",PROPERTY_HINT_RANGE,"-64,64,1",usage));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2,"shadow2/offset",PROPERTY_HINT_NONE,"",usage));
+ p_list->push_back(PropertyInfo(Variant::COLOR,"shadow2/color",PROPERTY_HINT_NONE,"",usage));
+ p_list->push_back(PropertyInfo(Variant::REAL,"shadow2/transition",PROPERTY_HINT_EXP_EASING,"",usage));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::INT,"color/mode",PROPERTY_HINT_ENUM,"White,Color,Gradient,Gradient Image",usage));
+ if (color_type==COLOR_CUSTOM) {
+ p_list->push_back(PropertyInfo(Variant::COLOR,"color/color",PROPERTY_HINT_NONE,"",usage));
+
+ }
+ if (color_type==COLOR_GRADIENT_RANGE) {
+ p_list->push_back(PropertyInfo(Variant::COLOR,"color/begin",PROPERTY_HINT_NONE,"",usage));
+ p_list->push_back(PropertyInfo(Variant::COLOR,"color/end",PROPERTY_HINT_NONE,"",usage));
+ }
+ if (color_type==COLOR_GRADIENT_IMAGE) {
+ p_list->push_back(PropertyInfo(Variant::STRING,"color/image",PROPERTY_HINT_FILE,"",usage));
+ }
+ p_list->push_back(PropertyInfo(Variant::BOOL,"color/monochrome",PROPERTY_HINT_NONE,"",usage));
}
- if (color_type==COLOR_GRADIENT_RANGE) {
- p_list->push_back(PropertyInfo(Variant::COLOR,"color/begin"));
- p_list->push_back(PropertyInfo(Variant::COLOR,"color/end"));
- }
- if (color_type==COLOR_GRADIENT_IMAGE) {
- p_list->push_back(PropertyInfo(Variant::STRING,"color/image",PROPERTY_HINT_FILE));
- }
- p_list->push_back(PropertyInfo(Variant::BOOL,"color/monochrome"));
+
p_list->push_back(PropertyInfo(Variant::BOOL,"advanced/round_advance"));
p_list->push_back(PropertyInfo(Variant::BOOL,"advanced/disable_filter"));
@@ -307,6 +333,7 @@ public:
gradient_end=Color(0.5,0.5,0.5,1);
color_use_monochrome=false;
+ font_mode=FONT_BITMAP;
round_advance=true;
disable_filter=false;
@@ -314,6 +341,8 @@ public:
_EditorFontImportOptions() {
+ font_mode=FONT_BITMAP;
+
char_extra_spacing=0;
top_extra_spacing=0;
bottom_extra_spacing=0;
@@ -350,8 +379,8 @@ class EditorFontImportDialog : public ConfirmationDialog {
OBJ_TYPE(EditorFontImportDialog, ConfirmationDialog);
- LineEditFileChooser *source;
- LineEditFileChooser *dest;
+ EditorLineEditFileChooser *source;
+ EditorLineEditFileChooser *dest;
SpinBox *font_size;
LineEdit *test_string;
ColorPickerButton *test_color;
@@ -377,7 +406,10 @@ class EditorFontImportDialog : public ConfirmationDialog {
imd->set_option(opt,v);
}
- imd->add_source(EditorImportPlugin::validate_source_path(source->get_line_edit()->get_text()));
+ String src_path = EditorImportPlugin::validate_source_path(source->get_line_edit()->get_text());
+ //print_line("pre src path "+source->get_line_edit()->get_text());
+ //print_line("src path "+src_path);
+ imd->add_source(src_path);
imd->set_option("font/size",font_size->get_val());
return imd;
@@ -499,7 +531,7 @@ class EditorFontImportDialog : public ConfirmationDialog {
Error err = plugin->import(dest->get_line_edit()->get_text(),rimd);
if (err!=OK) {
- error_dialog->set_text("Could't save font.");
+ error_dialog->set_text("Couldn't save font.");
error_dialog->popup_centered(Size2(200,100));
return;
}
@@ -580,9 +612,9 @@ public:
hbc->add_child(vbr);
vbr->set_h_size_flags(SIZE_EXPAND_FILL);
- source = memnew( LineEditFileChooser );
- source->get_file_dialog()->set_access(FileDialog::ACCESS_FILESYSTEM);
- source->get_file_dialog()->set_mode(FileDialog::MODE_OPEN_FILE);
+ source = memnew( EditorLineEditFileChooser );
+ source->get_file_dialog()->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
+ source->get_file_dialog()->set_mode(EditorFileDialog::MODE_OPEN_FILE);
source->get_file_dialog()->add_filter("*.ttf;TrueType");
source->get_file_dialog()->add_filter("*.otf;OpenType");
source->get_line_edit()->connect("text_entered",this,"_src_changed");
@@ -594,7 +626,7 @@ public:
font_size->set_max(256);
font_size->set_val(16);
font_size->connect("value_changed",this,"_font_size_changed");
- dest = memnew( LineEditFileChooser );
+ dest = memnew( EditorLineEditFileChooser );
//
List<String> fl;
Ref<Font> font= memnew(Font);
@@ -621,6 +653,7 @@ public:
vbl->add_spacer();
vbl->add_margin_child("Test: ",testhb);
+ /*
HBoxContainer *upd_hb = memnew( HBoxContainer );
// vbl->add_child(upd_hb);
upd_hb->add_spacer();
@@ -628,7 +661,7 @@ public:
upd_hb->add_child(update);
update->set_text("Update");
update->connect("pressed",this,"_update");
-
+*/
options = memnew( _EditorFontImportOptions );
prop_edit = memnew( PropertyEditor() );
vbr->add_margin_child("Options:",prop_edit,true);
@@ -706,6 +739,137 @@ struct _EditorKerningKey {
};
+
+static unsigned char get_SDF_radial(
+ unsigned char *fontmap,
+ int w, int h,
+ int x, int y,
+ int max_radius )
+{
+ // hideous brute force method
+ float d2 = max_radius*max_radius+1.0;
+ unsigned char v = fontmap[x+y*w];
+ for( int radius = 1; (radius <= max_radius) && (radius*radius < d2); ++radius )
+ {
+ int line, lo, hi;
+ // north
+ line = y - radius;
+ if( (line >= 0) && (line < h) )
+ {
+ lo = x - radius;
+ hi = x + radius;
+ if( lo < 0 ) { lo = 0; }
+ if( hi >= w ) { hi = w-1; }
+ int idx = line * w + lo;
+ for( int i = lo; i <= hi; ++i )
+ {
+ // check this pixel
+ if( fontmap[idx] != v )
+ {
+ float nx = i - x;
+ float ny = line - y;
+ float nd2 = nx*nx+ny*ny;
+ if( nd2 < d2 )
+ {
+ d2 = nd2;
+ }
+ }
+ // move on
+ ++idx;
+ }
+ }
+ // south
+ line = y + radius;
+ if( (line >= 0) && (line < h) )
+ {
+ lo = x - radius;
+ hi = x + radius;
+ if( lo < 0 ) { lo = 0; }
+ if( hi >= w ) { hi = w-1; }
+ int idx = line * w + lo;
+ for( int i = lo; i <= hi; ++i )
+ {
+ // check this pixel
+ if( fontmap[idx] != v )
+ {
+ float nx = i - x;
+ float ny = line - y;
+ float nd2 = nx*nx+ny*ny;
+ if( nd2 < d2 )
+ {
+ d2 = nd2;
+ }
+ }
+ // move on
+ ++idx;
+ }
+ }
+ // west
+ line = x - radius;
+ if( (line >= 0) && (line < w) )
+ {
+ lo = y - radius + 1;
+ hi = y + radius - 1;
+ if( lo < 0 ) { lo = 0; }
+ if( hi >= h ) { hi = h-1; }
+ int idx = lo * w + line;
+ for( int i = lo; i <= hi; ++i )
+ {
+ // check this pixel
+ if( fontmap[idx] != v )
+ {
+ float nx = line - x;
+ float ny = i - y;
+ float nd2 = nx*nx+ny*ny;
+ if( nd2 < d2 )
+ {
+ d2 = nd2;
+ }
+ }
+ // move on
+ idx += w;
+ }
+ }
+ // east
+ line = x + radius;
+ if( (line >= 0) && (line < w) )
+ {
+ lo = y - radius + 1;
+ hi = y + radius - 1;
+ if( lo < 0 ) { lo = 0; }
+ if( hi >= h ) { hi = h-1; }
+ int idx = lo * w + line;
+ for( int i = lo; i <= hi; ++i )
+ {
+ // check this pixel
+ if( fontmap[idx] != v )
+ {
+ float nx = line - x;
+ float ny = i - y;
+ float nd2 = nx*nx+ny*ny;
+ if( nd2 < d2 )
+ {
+ d2 = nd2;
+ }
+ }
+ // move on
+ idx += w;
+ }
+ }
+ }
+ d2 = sqrtf( d2 );
+ if( v==0 )
+ {
+ d2 = -d2;
+ }
+ d2 *= 127.5 / max_radius;
+ d2 += 127.5;
+ if( d2 < 0.0 ) d2 = 0.0;
+ if( d2 > 255.0 ) d2 = 255.0;
+ return (unsigned char)(d2 + 0.5);
+}
+
+
Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata>& p_from, const String &p_existing) {
Ref<ResourceImportMetadata> from = p_from;
@@ -754,7 +918,11 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
}
- error = FT_Set_Pixel_Sizes(face,0,size);
+ int font_mode = from->get_option("mode/mode");
+
+ int scaler=(font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD)?16:1;
+
+ error = FT_Set_Pixel_Sizes(face,0,size*scaler);
FT_GlyphSlot slot = face->glyph;
@@ -820,9 +988,9 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
{
bool skip=false;
- error = FT_Load_Char( face, charcode, FT_LOAD_RENDER );
+ error = FT_Load_Char( face, charcode, font_mode==_EditorFontImportOptions::FONT_BITMAP?FT_LOAD_RENDER:FT_LOAD_MONOCHROME );
if (error) skip=true;
- else error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
+ else error = FT_Render_Glyph( face->glyph, font_mode==_EditorFontImportOptions::FONT_BITMAP?ft_render_mode_normal:ft_render_mode_mono );
if (error) {
skip=true;
} else if (!skip) {
@@ -847,28 +1015,36 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
}
_EditorFontData * fdata = memnew( _EditorFontData );
- fdata->bitmap.resize( slot->bitmap.width*slot->bitmap.rows );
- fdata->width=slot->bitmap.width;
- fdata->height=slot->bitmap.rows;
- fdata->character=charcode;
- fdata->glyph=FT_Get_Char_Index(face,charcode);
- if (charcode=='x')
- xsize=slot->bitmap.width;
- if (charcode<127) {
- if (slot->bitmap_top>max_up) {
+ int w = slot->bitmap.width;
+ int h = slot->bitmap.rows;
+ int p = slot->bitmap.pitch;
- max_up=slot->bitmap_top;
- }
+ //print_line("W: "+itos(w)+" P: "+itos(slot->bitmap.pitch));
+ if (font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD) {
- if ( (slot->bitmap_top - fdata->height)<max_down ) {
+ // oversize the holding buffer so I can smooth it!
+ int sw = w + scaler * 4;
+ int sh = h + scaler * 4;
+ // do the SDF
+ int sdfw = sw / scaler;
+ int sdfh = sh / scaler;
- max_down=slot->bitmap_top - fdata->height;
- }
+ fdata->width=sdfw;
+ fdata->height=sdfh;
+ } else {
+ fdata->width=w;
+ fdata->height=h;
}
+ fdata->character=charcode;
+ fdata->glyph=FT_Get_Char_Index(face,charcode);
+ if (charcode=='x')
+ xsize=w/scaler;
+
+
fdata->valign=slot->bitmap_top;
fdata->halign=slot->bitmap_left;
@@ -878,12 +1054,85 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
else
fdata->advance=slot->advance.x/float(1<<6);
+ if (font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD) {
+
+ fdata->halign = fdata->halign / scaler - 1.5;
+ fdata->valign = fdata->valign / scaler + 1.5;
+ fdata->advance/=scaler;
+
+ }
+
fdata->advance+=font_spacing;
- for (int i=0;i<slot->bitmap.width;i++) {
- for (int j=0;j<slot->bitmap.rows;j++) {
- fdata->bitmap[j*slot->bitmap.width+i]=slot->bitmap.buffer[j*slot->bitmap.width+i];
+ if (charcode<127) {
+ int top = fdata->valign;
+ int hmax = h/scaler;
+
+ if (top>max_up) {
+
+ max_up=top;
+ }
+
+
+ if ( (top - hmax)<max_down ) {
+
+ max_down=top - hmax;
+ }
+ }
+
+ if (font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD) {
+
+
+ // oversize the holding buffer so I can smooth it!
+ int sw = w + scaler * 4;
+ int sh = h + scaler * 4;
+
+ unsigned char *smooth_buf = new unsigned char[sw*sh];
+
+ for( int i = 0; i < sw*sh; ++i ) {
+ smooth_buf[i] = 0;
+ }
+
+ // copy the glyph into the buffer to be smoothed
+ unsigned char *buf = slot->bitmap.buffer;
+ for( int j = 0; j < h; ++j ) {
+ for( int i = 0; i < w; ++i ) {
+ smooth_buf[scaler*2+i+(j+scaler*2)*sw] = 255 * ((buf[j*p+(i>>3)] >> (7 - (i & 7))) & 1);
+ }
+ }
+
+ // do the SDF
+ int sdfw = fdata->width;
+ int sdfh = fdata->height;
+
+ fdata->bitmap.resize( sdfw*sdfh );
+
+ for( int j = 0; j < sdfh; ++j ) {
+ for( int i = 0; i < sdfw; ++i ) {
+ int pd_idx = j*sdfw+i;
+
+ //fdata->bitmap[j*slot->bitmap.width+i]=slot->bitmap.buffer[j*slot->bitmap.width+i];
+
+ fdata->bitmap[pd_idx] =
+ //get_SDF
+ get_SDF_radial
+ ( smooth_buf, sw, sh,
+ i*scaler + (scaler >>1), j*scaler + (scaler >>1),
+ 2*scaler );
+
+ }
+ }
+
+ delete [] smooth_buf;
+
+ } else {
+ fdata->bitmap.resize( slot->bitmap.width*slot->bitmap.rows );
+ for (int i=0;i<slot->bitmap.width;i++) {
+ for (int j=0;j<slot->bitmap.rows;j++) {
+
+ fdata->bitmap[j*slot->bitmap.width+i]=slot->bitmap.buffer[j*slot->bitmap.width+i];
+ }
}
}
@@ -904,9 +1153,10 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
spd->ofs_x=0;
spd->ofs_y=0;
- if (!FT_Load_Char( face, ' ', FT_LOAD_RENDER ) && !FT_Render_Glyph( face->glyph, ft_render_mode_normal )) {
+ if (!FT_Load_Char( face, ' ', FT_LOAD_RENDER ) && !FT_Render_Glyph( face->glyph, font_mode==_EditorFontImportOptions::FONT_BITMAP?ft_render_mode_normal:ft_render_mode_mono )) {
spd->advance = slot->advance.x>>6; //round to nearest or store as float
+ spd->advance/=scaler;
spd->advance+=font_spacing;
} else {
@@ -955,7 +1205,7 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
if (kern==0)
continue;
- kerning_map[kpk]=kern;
+ kerning_map[kpk]=kern/scaler;
}
}
}
@@ -1079,7 +1329,7 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
pixels.resize(s.x*s.y*4);
DVector<uint8_t>::Write w = pixels.write();
- print_line("val: "+itos(font_data_list[i]->valign));
+ //print_line("val: "+itos(font_data_list[i]->valign));
for(int y=0;y<s.height;y++) {
int yc=CLAMP(y-o.y+font_data_list[i]->valign,0,height-1);
@@ -1284,6 +1534,7 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
font->clear();
font->set_height(height+bottom_space+top_space);
font->set_ascent(ascent+top_space);
+ font->set_distance_field_hint(font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD);
//register texures
{
diff --git a/tools/editor/io_plugins/editor_font_import_plugin.h b/tools/editor/io_plugins/editor_font_import_plugin.h
index ac3b4eb0fe..451f01080e 100644
--- a/tools/editor/io_plugins/editor_font_import_plugin.h
+++ b/tools/editor/io_plugins/editor_font_import_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/io_plugins/editor_import_collada.cpp b/tools/editor/io_plugins/editor_import_collada.cpp
index 529ed3374b..d57cff850e 100644
--- a/tools/editor/io_plugins/editor_import_collada.cpp
+++ b/tools/editor/io_plugins/editor_import_collada.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -39,6 +39,7 @@
#include "scene/resources/packed_scene.h"
#include "os/os.h"
#include "tools/editor/editor_node.h"
+#include <iostream>
struct ColladaImport {
@@ -83,7 +84,7 @@ struct ColladaImport {
Error _create_scene(Collada::Node *p_node, Spatial *p_parent);
Error _create_resources(Collada::Node *p_node);
Error _create_material(const String& p_material);
- Error _create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_data, const Collada::MorphControllerData *p_morph_data);
+ Error _create_mesh_surfaces(bool p_optimize,Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_data, const Collada::MorphControllerData *p_morph_data,Vector<Ref<Mesh> > p_morph_meshes=Vector<Ref<Mesh> >());
Error load(const String& p_path, int p_flags, bool p_force_make_tangents=false);
void _fix_param_animation_tracks();
void create_animation(int p_clip,bool p_make_tracks_in_all_bones);
@@ -285,13 +286,16 @@ Error ColladaImport::_create_scene(Collada::Node *p_node, Spatial *p_parent) {
case Collada::CameraData::MODE_ORTHOGONAL: {
- if (cd.orthogonal.x_mag) {
+ if (cd.orthogonal.y_mag) {
- camera->set_orthogonal(cd.orthogonal.x_mag,cd.z_near,cd.z_far);
+ camera->set_keep_aspect_mode(Camera::KEEP_HEIGHT);
+ camera->set_orthogonal(cd.orthogonal.y_mag*2.0 ,cd.z_near,cd.z_far);
- } else if (!cd.orthogonal.x_mag && cd.orthogonal.y_mag) {
+ } else if (!cd.orthogonal.y_mag && cd.orthogonal.x_mag) {
- camera->set_orthogonal(cd.orthogonal.y_mag * cd.aspect,cd.z_near,cd.z_far);
+
+ camera->set_keep_aspect_mode(Camera::KEEP_WIDTH);
+ camera->set_orthogonal(cd.orthogonal.x_mag*2.0,cd.z_near,cd.z_far);
}
} break;
@@ -585,7 +589,7 @@ static void _generate_tangents_and_binormals(const DVector<int>& p_indices,const
}
}
-Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *skin_controller, const Collada::MorphControllerData *p_morph_data) {
+Error ColladaImport::_create_mesh_surfaces(bool p_optimize,Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *skin_controller, const Collada::MorphControllerData *p_morph_data,Vector<Ref<Mesh> > p_morph_meshes) {
bool local_xform_mirror=p_local_xform.basis.determinant() < 0;
@@ -706,10 +710,126 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co
//find largest source..
+ /************************/
+ /* ADD WEIGHTS IF EXIST */
+ /************************/
+
+ Map<int,Vector<Collada::Vertex::Weight> > pre_weights;
+
+ bool has_weights=false;
+
+ if (skin_controller) {
+
+ const Collada::SkinControllerData::Source *weight_src=NULL;
+ int weight_ofs=0;
+
+ if (skin_controller->weights.sources.has("WEIGHT")) {
+
+ String weight_id = skin_controller->weights.sources["WEIGHT"].source;
+ weight_ofs = skin_controller->weights.sources["WEIGHT"].offset;
+ if (skin_controller->sources.has(weight_id)) {
+
+ weight_src = &skin_controller->sources[weight_id];
+
+ }
+ }
+
+ int joint_ofs=0;
+
+ if (skin_controller->weights.sources.has("JOINT")) {
+
+ joint_ofs = skin_controller->weights.sources["JOINT"].offset;
+ }
+
+ //should be OK, given this was pre-checked.
+
+ int index_ofs=0;
+ int wstride = skin_controller->weights.sources.size();
+ for(int w_i=0;w_i<skin_controller->weights.sets.size();w_i++) {
+
+ int amount = skin_controller->weights.sets[w_i];
+
+ Vector<Collada::Vertex::Weight> weights;
+
+ for (int a_i=0;a_i<amount;a_i++) {
+
+ Collada::Vertex::Weight w;
+
+ int read_from = index_ofs+a_i*wstride;
+ ERR_FAIL_INDEX_V(read_from+wstride-1,skin_controller->weights.indices.size(),ERR_INVALID_DATA);
+ int weight_index = skin_controller->weights.indices[read_from+weight_ofs];
+ ERR_FAIL_INDEX_V(weight_index,weight_src->array.size(),ERR_INVALID_DATA);
+
+ w.weight = weight_src->array[weight_index];
+
+ int bone_index = skin_controller->weights.indices[read_from+joint_ofs];
+ if (bone_index==-1)
+ continue; //ignore this weight (refers to bind shape)
+ ERR_FAIL_INDEX_V(bone_index,bone_remap.size(),ERR_INVALID_DATA);
+
+ w.bone_idx=bone_remap[bone_index];
+
+
+ weights.push_back(w);
+ }
+
+ /* FIX WEIGHTS */
+
+
+
+ weights.sort();
+
+ if (weights.size()>4) {
+ //cap to 4 and make weights add up 1
+ weights.resize(4);
+
+ }
+
+ //make sure weights allways add up to 1
+ float total=0;
+ for(int i=0;i<weights.size();i++)
+ total+=weights[i].weight;
+ if (total)
+ for(int i=0;i<weights.size();i++)
+ weights[i].weight/=total;
+
+ if (weights.size()==0 || total==0) { //if nothing, add a weight to bone 0
+ //no weights assigned
+ Collada::Vertex::Weight w;
+ w.bone_idx=0;
+ w.weight=1.0;
+ weights.clear();
+ weights.push_back(w);
+
+ }
+
+ pre_weights[w_i]=weights;
+
+ /*
+ for(Set<int>::Element *E=vertex_map[w_i].front();E;E=E->next()) {
+
+ int dst = E->get();
+ ERR_EXPLAIN("invalid vertex index in array");
+ ERR_FAIL_INDEX_V(dst,vertex_array.size(),ERR_INVALID_DATA);
+ vertex_array[dst].weights=weights;
+
+ }*/
+
+
+
+
+ index_ofs+=wstride*amount;
+
+ }
+
+ //vertices need to be localized
+ has_weights=true;
+
+ }
Set<Collada::Vertex> vertex_set; //vertex set will be the vertices
List<int> indices_list; //indices will be the indices
- Map<int,Set<int> > vertex_map; //map vertices (for setting skinning/morph)
+ //Map<int,Set<int> > vertex_map; //map vertices (for setting skinning/morph)
/**************************/
/* CREATE PRIMITIVE ARRAY */
@@ -746,14 +866,19 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co
ERR_FAIL_INDEX_V(src,p.indices.size(),ERR_INVALID_DATA);
Collada::Vertex vertex;
- if (p_morph_data)
+ if (!p_optimize)
vertex.uid=vertidx++;
+
+
int vertex_index=p.indices[src+vertex_ofs]; //used for index field (later used by controllers)
int vertex_pos = (vertex_src->stride?vertex_src->stride:3) * vertex_index;
ERR_FAIL_INDEX_V(vertex_pos,vertex_src->array.size(),ERR_INVALID_DATA);
vertex.vertex=Vector3(vertex_src->array[vertex_pos+0],vertex_src->array[vertex_pos+1],vertex_src->array[vertex_pos+2]);
+ if (pre_weights.has(vertex_index)) {
+ vertex.weights=pre_weights[vertex_index];
+ }
if (normal_src) {
@@ -832,9 +957,9 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co
vertex_set.insert(vertex);
}
- if (!vertex_map.has(vertex_index))
+ /* if (!vertex_map.has(vertex_index))
vertex_map[vertex_index]=Set<int>();
- vertex_map[vertex_index].insert(index); //should be outside..
+ vertex_map[vertex_index].insert(index); //should be outside..*/
//build triangles if needed
if (j==0)
prev2[0]=index;
@@ -870,120 +995,10 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co
vertex_array[F->get().idx]=F->get();
}
- /************************/
- /* ADD WEIGHTS IF EXIST */
- /************************/
-
-
- bool has_weights=false;
-
- if (skin_controller) {
-
- const Collada::SkinControllerData::Source *weight_src=NULL;
- int weight_ofs=0;
- if (skin_controller->weights.sources.has("WEIGHT")) {
-
- String weight_id = skin_controller->weights.sources["WEIGHT"].source;
- weight_ofs = skin_controller->weights.sources["WEIGHT"].offset;
- if (skin_controller->sources.has(weight_id)) {
-
- weight_src = &skin_controller->sources[weight_id];
-
- }
- }
-
- int joint_ofs=0;
-
- if (skin_controller->weights.sources.has("JOINT")) {
-
- joint_ofs = skin_controller->weights.sources["JOINT"].offset;
- }
-
- //should be OK, given this was pre-checked.
-
- int index_ofs=0;
- int wstride = skin_controller->weights.sources.size();
- for(int w_i=0;w_i<skin_controller->weights.sets.size();w_i++) {
-
- int amount = skin_controller->weights.sets[w_i];
-
- if (vertex_map.has(w_i)) { //vertex may no longer be here, don't bother converting
-
- Vector<Collada::Vertex::Weight> weights;
-
- for (int a_i=0;a_i<amount;a_i++) {
-
- Collada::Vertex::Weight w;
-
- int read_from = index_ofs+a_i*wstride;
- ERR_FAIL_INDEX_V(read_from+wstride-1,skin_controller->weights.indices.size(),ERR_INVALID_DATA);
- int weight_index = skin_controller->weights.indices[read_from+weight_ofs];
- ERR_FAIL_INDEX_V(weight_index,weight_src->array.size(),ERR_INVALID_DATA);
-
- w.weight = weight_src->array[weight_index];
-
- int bone_index = skin_controller->weights.indices[read_from+joint_ofs];
- if (bone_index==-1)
- continue; //ignore this weight (refers to bind shape)
- ERR_FAIL_INDEX_V(bone_index,bone_remap.size(),ERR_INVALID_DATA);
-
- w.bone_idx=bone_remap[bone_index];
-
-
- weights.push_back(w);
- }
-
- /* FIX WEIGHTS */
-
-
-
- weights.sort();
-
- if (weights.size()>4) {
- //cap to 4 and make weights add up 1
- weights.resize(4);
-
- }
-
- //make sure weights allways add up to 1
- float total=0;
- for(int i=0;i<weights.size();i++)
- total+=weights[i].weight;
- if (total)
- for(int i=0;i<weights.size();i++)
- weights[i].weight/=total;
-
- if (weights.size()==0 || total==0) { //if nothing, add a weight to bone 0
- //no weights assigned
- Collada::Vertex::Weight w;
- w.bone_idx=0;
- w.weight=1.0;
- weights.clear();
- weights.push_back(w);
-
- }
-
-
- for(Set<int>::Element *E=vertex_map[w_i].front();E;E=E->next()) {
-
- int dst = E->get();
- ERR_EXPLAIN("invalid vertex index in array");
- ERR_FAIL_INDEX_V(dst,vertex_array.size(),ERR_INVALID_DATA);
- vertex_array[dst].weights=weights;
-
- }
-
- } else {
- //zzprint_line("no vertex found for index "+itos(w_i));
- }
-
- index_ofs+=wstride*amount;
-
- }
-
- //vertices need to be localized
+ if (has_weights) {
+ //if skeleton, localize
Transform local_xform = p_local_xform;
for(int i=0;i<vertex_array.size();i++) {
@@ -996,11 +1011,9 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co
//vertex_array[i].tangent.normal*=-1.0;
}
}
-
- has_weights=true;
-
}
+
DVector<int> index_array;
index_array.resize(indices_list.size());
DVector<int>::Write index_arrayw = index_array.write();
@@ -1272,7 +1285,7 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co
////////////////////////////
// THEN THE MORPH TARGETS //
////////////////////////////
-
+#if 0
if (p_morph_data) {
//add morphie target
@@ -1354,8 +1367,63 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co
vertw = DVector<Vector3>::Write();
DVector<Vector3> normals;
DVector<float> tangents;
+ print_line("vertex source id: "+vertex_src_id);
+ if(md.vertices[vertex_src_id].sources.has("NORMAL")){
+ //has normals
+ normals.resize(vlen);
+ //std::cout << "has normals" << std::endl;
+ String normal_src_id = md.vertices[vertex_src_id].sources["NORMAL"];
+ //std::cout << "normals source: "<< normal_src_id.utf8().get_data() <<std::endl;
+ ERR_FAIL_COND_V(!md.sources.has(normal_src_id),ERR_INVALID_DATA);
+
+ const Collada::MeshData::Source *m=&md.sources[normal_src_id];
+
+ ERR_FAIL_COND_V( m->array.size() != vertex_src->array.size(), ERR_INVALID_DATA);
+ int stride=m->stride;
+ if (stride==0)
+ stride=3;
+
+
+ //read normals from morph target
+ DVector<Vector3>::Write vertw = normals.write();
+
+ for(int m_i=0;m_i<m->array.size()/stride;m_i++) {
+
+ int pos = m_i*stride;
+ Vector3 vtx( m->array[pos+0], m->array[pos+1], m->array[pos+2] );
+
+ #ifndef NO_UP_AXIS_SWAP
+ if (collada.state.up_axis==Vector3::AXIS_Z) {
+
+ SWAP( vtx.z, vtx.y );
+ vtx.z = -vtx.z;
+
+ }
+ #endif
+
+ Collada::Vertex vertex;
+ vertex.vertex=vtx;
+ vertex.fix_unit_scale(collada);
+ vtx=vertex.vertex;
+
+ vtx = p_local_xform.xform(vtx);
+
- _generate_normals(index_array,vertices,normals);
+ if (vertex_map.has(m_i)) { //vertex may no longer be here, don't bother converting
+
+
+ for (Set<int> ::Element *E=vertex_map[m_i].front() ; E; E=E->next() ) {
+
+ vertw[E->get()]=vtx;
+ }
+ }
+ }
+
+ print_line("using built-in normals");
+ }else{
+ print_line("generating normals");
+ _generate_normals(index_array,vertices,normals);//no normals
+ }
if (final_tangent_array.size() && final_uv_array.size()) {
_generate_tangents_and_binormals(index_array,vertices,final_uv_array,normals,tangents);
@@ -1380,6 +1448,17 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co
}
+#endif
+ for(int mi=0;mi<p_morph_meshes.size();mi++) {
+
+ // print_line("want surface "+itos(mi)+" has "+itos(p_morph_meshes[mi]->get_surface_count()));
+ Array a = p_morph_meshes[mi]->surface_get_arrays(surface);
+ a[Mesh::ARRAY_BONES]=Variant();
+ a[Mesh::ARRAY_WEIGHTS]=Variant();
+ a[Mesh::ARRAY_INDEX]=Variant();
+ //a.resize(Mesh::ARRAY_MAX); //no need for index
+ mr.push_back(a);
+ }
p_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,d,mr);
@@ -1510,17 +1589,21 @@ Error ColladaImport::_create_resources(Collada::Node *p_node) {
String meshid;
Transform apply_xform;
Vector<int> bone_remap;
+ Vector<Ref<Mesh> > morphs;
print_line("mesh: "+String(mi->get_name()));
if (ng->controller) {
print_line("has controller");
- if (collada.state.skin_controller_data_map.has(ng->source)) {
+ String ngsource = ng->source;
- ERR_FAIL_COND_V(!collada.state.skin_controller_data_map.has(ng->source),ERR_INVALID_DATA);
- skin=&collada.state.skin_controller_data_map[ng->source];
+ if (collada.state.skin_controller_data_map.has(ngsource)) {
+
+
+ ERR_FAIL_COND_V(!collada.state.skin_controller_data_map.has(ngsource),ERR_INVALID_DATA);
+ skin=&collada.state.skin_controller_data_map[ngsource];
Vector<String> skeletons = ng->skeletons;
@@ -1543,7 +1626,10 @@ Error ColladaImport::_create_resources(Collada::Node *p_node) {
if (collada.state.morph_controller_data_map.has(meshid)) {
//it's a morph!!
morph = &collada.state.morph_controller_data_map[meshid];
+ ngsource=meshid;
meshid=morph->mesh;
+ } else {
+ ngsource="";
}
if (apply_mesh_xform_to_vertices) {
@@ -1574,15 +1660,48 @@ Error ColladaImport::_create_resources(Collada::Node *p_node) {
ERR_FAIL_COND_V( !bone_remap_map.has(str), ERR_INVALID_DATA );
bone_remap[i]=bone_remap_map[str];
}
- } else if (collada.state.morph_controller_data_map.has(ng->source)) {
- print_line("is morph "+ng->source);
+ }
+
+ if (collada.state.morph_controller_data_map.has(ngsource)) {
+ print_line("is morph "+ngsource);
//it's a morph!!
- morph = &collada.state.morph_controller_data_map[ng->source];
+ morph = &collada.state.morph_controller_data_map[ngsource];
meshid=morph->mesh;
printf("KKmorph: %p\n",morph);
print_line("morph mshid: "+meshid);
- } else {
- ERR_EXPLAIN("Controller Instance Source '"+ng->source+"' is neither skin or morph!");
+
+ Vector<String> targets;
+
+ morph->targets.has("MORPH_TARGET");
+ String target = morph->targets["MORPH_TARGET"];
+ bool valid=false;
+ if (morph->sources.has(target)) {
+ valid=true;
+ Vector<String> names = morph->sources[target].sarray;
+ for(int i=0;i<names.size();i++) {
+
+ String meshid=names[i];
+ if (collada.state.mesh_data_map.has(meshid)) {
+ Ref<Mesh> mesh=Ref<Mesh>(memnew( Mesh ));
+ const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid];
+ Error err = _create_mesh_surfaces(false,mesh,ng->material_map,meshdata,apply_xform,bone_remap,skin,NULL);
+ ERR_FAIL_COND_V(err,err);
+
+ morphs.push_back(mesh);
+ } else {
+ valid=false;
+ }
+ }
+ }
+
+ if (!valid)
+ morphs.clear();
+
+ ngsource="";
+ }
+
+ if (ngsource!=""){
+ ERR_EXPLAIN("Controller Instance Source '"+ngsource+"' is neither skin or morph!");
ERR_FAIL_V( ERR_INVALID_DATA );
}
@@ -1603,7 +1722,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node) {
mesh=Ref<Mesh>(memnew( Mesh ));
const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid];
mesh->set_name( meshdata.name );
- Error err = _create_mesh_surfaces(mesh,ng->material_map,meshdata,apply_xform,bone_remap,skin,morph);
+ Error err = _create_mesh_surfaces(morphs.size()==0,mesh,ng->material_map,meshdata,apply_xform,bone_remap,skin,morph,morphs);
ERR_FAIL_COND_V(err,err);
mesh_cache[meshid]=mesh;
diff --git a/tools/editor/io_plugins/editor_import_collada.h b/tools/editor/io_plugins/editor_import_collada.h
index ae4cedeff6..243cd043a0 100644
--- a/tools/editor/io_plugins/editor_import_collada.h
+++ b/tools/editor/io_plugins/editor_import_collada.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/io_plugins/editor_mesh_import_plugin.cpp b/tools/editor/io_plugins/editor_mesh_import_plugin.cpp
index 7d6f400ccc..2139513025 100644
--- a/tools/editor/io_plugins/editor_mesh_import_plugin.cpp
+++ b/tools/editor/io_plugins/editor_mesh_import_plugin.cpp
@@ -1,6 +1,6 @@
#include "editor_mesh_import_plugin.h"
-#include "scene/gui/file_dialog.h"
+#include "tools/editor/editor_file_dialog.h"
#include "tools/editor/editor_dir_dialog.h"
#include "tools/editor/editor_node.h"
#include "tools/editor/property_editor.h"
@@ -105,7 +105,7 @@ public:
_EditorMeshImportOptions() {
generate_tangents=true;
- generate_normals=true;
+ generate_normals=false;
flip_faces=false;
smooth_shading=false;
weld_vertices=true;
@@ -126,7 +126,7 @@ class EditorMeshImportDialog : public ConfirmationDialog {
LineEdit *import_path;
LineEdit *save_path;
- FileDialog *file_select;
+ EditorFileDialog *file_select;
EditorDirDialog *save_select;
ConfirmationDialog *error_dialog;
PropertyEditor *option_editor;
@@ -300,16 +300,16 @@ public:
save_choose->connect("pressed", this,"_browse_target");
- file_select = memnew(FileDialog);
- file_select->set_access(FileDialog::ACCESS_FILESYSTEM);
+ file_select = memnew(EditorFileDialog);
+ file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
add_child(file_select);
- file_select->set_mode(FileDialog::MODE_OPEN_FILES);
+ file_select->set_mode(EditorFileDialog::MODE_OPEN_FILES);
file_select->connect("files_selected", this,"_choose_files");
file_select->add_filter("*.obj ; Wavefront OBJ");
save_select = memnew( EditorDirDialog );
add_child(save_select);
- // save_select->set_mode(FileDialog::MODE_OPEN_DIR);
+ // save_select->set_mode(EditorFileDialog::MODE_OPEN_DIR);
save_select->connect("dir_selected", this,"_choose_save_dir");
get_ok()->connect("pressed", this,"_import");
diff --git a/tools/editor/io_plugins/editor_sample_import_plugin.cpp b/tools/editor/io_plugins/editor_sample_import_plugin.cpp
index 377af8f179..7888246956 100644
--- a/tools/editor/io_plugins/editor_sample_import_plugin.cpp
+++ b/tools/editor/io_plugins/editor_sample_import_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,7 +27,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "editor_sample_import_plugin.h"
-#include "scene/gui/file_dialog.h"
+#include "tools/editor/editor_file_dialog.h"
#include "tools/editor/editor_dir_dialog.h"
#include "tools/editor/editor_node.h"
#include "tools/editor/property_editor.h"
@@ -35,6 +35,7 @@
#include "io/resource_saver.h"
#include "os/file_access.h"
#include "io/marshalls.h"
+#include "tools/editor/editor_settings.h"
class _EditorSampleImportOptions : public Object {
@@ -156,7 +157,7 @@ public:
edit_normalize=true;
edit_loop=false;
- compress_mode=COMPRESS_MODE_DISABLED;
+ compress_mode=COMPRESS_MODE_RAM;
compress_bitrate=COMPRESS_128;
}
@@ -171,7 +172,7 @@ class EditorSampleImportDialog : public ConfirmationDialog {
LineEdit *import_path;
LineEdit *save_path;
- FileDialog *file_select;
+ EditorFileDialog *file_select;
EditorDirDialog *save_select;
ConfirmationDialog *error_dialog;
PropertyEditor *option_editor;
@@ -254,6 +255,24 @@ public:
error_dialog->popup_centered(Size2(200,100));
}
+ if (save_path->get_text().strip_edges()=="") {
+ error_dialog->set_text("Target path is empty.");
+ error_dialog->popup_centered_minsize();
+ return;
+ }
+
+ if (!save_path->get_text().begins_with("res://")) {
+ error_dialog->set_text("Target path must be full resource path.");
+ error_dialog->popup_centered_minsize();
+ return;
+ }
+
+ if (!DirAccess::exists(save_path->get_text())) {
+ error_dialog->set_text("Target path must exist.");
+ error_dialog->popup_centered_minsize();
+ return;
+ }
+
for(int i=0;i<samples.size();i++) {
Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata );
@@ -345,16 +364,16 @@ public:
save_choose->connect("pressed", this,"_browse_target");
- file_select = memnew(FileDialog);
- file_select->set_access(FileDialog::ACCESS_FILESYSTEM);
+ file_select = memnew(EditorFileDialog);
+ file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
add_child(file_select);
- file_select->set_mode(FileDialog::MODE_OPEN_FILES);
+ file_select->set_mode(EditorFileDialog::MODE_OPEN_FILES);
file_select->connect("files_selected", this,"_choose_files");
file_select->add_filter("*.wav ; MS Waveform");
save_select = memnew( EditorDirDialog );
add_child(save_select);
- // save_select->set_mode(FileDialog::MODE_OPEN_DIR);
+ // save_select->set_mode(EditorFileDialog::MODE_OPEN_DIR);
save_select->connect("dir_selected", this,"_choose_save_dir");
get_ok()->connect("pressed", this,"_import");
@@ -562,8 +581,7 @@ Error EditorSampleImportPlugin::import(const String& p_path, const Ref<ResourceI
int compression = from->get_option("compress/mode");
bool force_mono = from->get_option("force/mono");
- if (compression==_EditorSampleImportOptions::COMPRESS_MODE_RAM)
- force_mono=true;
+
if (force_mono && chans==2) {
@@ -590,8 +608,47 @@ Error EditorSampleImportPlugin::import(const String& p_path, const Ref<ResourceI
if ( compression == _EditorSampleImportOptions::COMPRESS_MODE_RAM) {
dst_format=Sample::FORMAT_IMA_ADPCM;
+ if (chans==1) {
+ _compress_ima_adpcm(data,dst_data);
+ } else {
+
+ print_line("INTERLEAAVE!");
+
+
+
+ //byte interleave
+ Vector<float> left;
+ Vector<float> right;
- _compress_ima_adpcm(data,dst_data);
+ int tlen = data.size()/2;
+ left.resize(tlen);
+ right.resize(tlen);
+
+ for(int i=0;i<tlen;i++) {
+ left[i]=data[i*2+0];
+ right[i]=data[i*2+1];
+ }
+
+ DVector<uint8_t> bleft;
+ DVector<uint8_t> bright;
+
+ _compress_ima_adpcm(left,bleft);
+ _compress_ima_adpcm(right,bright);
+
+ int dl = bleft.size();
+ dst_data.resize( dl *2 );
+
+ DVector<uint8_t>::Write w=dst_data.write();
+ DVector<uint8_t>::Read rl=bleft.read();
+ DVector<uint8_t>::Read rr=bright.read();
+
+ for(int i=0;i<dl;i++) {
+ w[i*2+0]=rl[i];
+ w[i*2+1]=rr[i];
+ }
+ }
+
+// print_line("compressing ima-adpcm, resulting buffersize is "+itos(dst_data.size())+" from "+itos(data.size()));
} else {
@@ -691,7 +748,7 @@ void EditorSampleImportPlugin::_compress_ima_adpcm(const Vector<float>& p_data,D
*(out++) =0;
for (i=0;i<datalen;i++) {
- int step,diff,vpdiff,signed_nibble,p,mask;
+ int step,diff,vpdiff,mask;
uint8_t nibble;
int16_t xm_sample;
@@ -701,8 +758,8 @@ void EditorSampleImportPlugin::_compress_ima_adpcm(const Vector<float>& p_data,D
xm_sample=CLAMP(in[i]*32767.0,-32768,32767);
- if (xm_sample==32767 || xm_sample==-32768)
- printf("clippy!\n",xm_sample);
+ //if (xm_sample==32767 || xm_sample==-32768)
+ // printf("clippy!\n",xm_sample);
}
// xm_sample=xm_sample+xm_prev;
@@ -737,10 +794,10 @@ void EditorSampleImportPlugin::_compress_ima_adpcm(const Vector<float>& p_data,D
prev+=vpdiff ;
if (prev > 32767) {
- printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip up %i\n",i,xm_sample,prev,diff,vpdiff,prev);
+ //printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip up %i\n",i,xm_sample,prev,diff,vpdiff,prev);
prev=32767;
} else if (prev < -32768) {
- printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip down %i\n",i,xm_sample,prev,diff,vpdiff,prev);
+ //printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip down %i\n",i,xm_sample,prev,diff,vpdiff,prev);
prev = -32768 ;
}
@@ -762,9 +819,54 @@ void EditorSampleImportPlugin::_compress_ima_adpcm(const Vector<float>& p_data,D
}
+
+EditorSampleImportPlugin* EditorSampleImportPlugin::singleton=NULL;
+
+
+
EditorSampleImportPlugin::EditorSampleImportPlugin(EditorNode* p_editor) {
+ singleton=this;
dialog = memnew( EditorSampleImportDialog(this));
p_editor->get_gui_base()->add_child(dialog);
}
+Vector<uint8_t> EditorSampleExportPlugin::custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform) {
+
+
+
+ if (EditorImportExport::get_singleton()->sample_get_action()==EditorImportExport::SAMPLE_ACTION_NONE || p_path.extension().to_lower()!="wav") {
+
+ return Vector<uint8_t>();
+ }
+
+ Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata );
+
+ imd->add_source(EditorImportPlugin::validate_source_path(p_path));
+
+ imd->set_option("force/8_bit",false);
+ imd->set_option("force/mono",false);
+ imd->set_option("force/max_rate",true);
+ imd->set_option("force/max_rate_hz",EditorImportExport::get_singleton()->sample_get_max_hz());
+ imd->set_option("edit/trim",EditorImportExport::get_singleton()->sample_get_trim());
+ imd->set_option("edit/normalize",false);
+ imd->set_option("edit/loop",false);
+ imd->set_option("compress/mode",1);
+
+ String savepath = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp/smpconv.smp");
+ Error err = EditorSampleImportPlugin::singleton->import(savepath,imd);
+
+
+ ERR_FAIL_COND_V(err!=OK,Vector<uint8_t>());
+
+ p_path=p_path.basename()+".smp";
+ return FileAccess::get_file_as_array(savepath);
+
+}
+
+
+EditorSampleExportPlugin::EditorSampleExportPlugin() {
+
+}
+
+
diff --git a/tools/editor/io_plugins/editor_sample_import_plugin.h b/tools/editor/io_plugins/editor_sample_import_plugin.h
index 176dece0d5..89319affa0 100644
--- a/tools/editor/io_plugins/editor_sample_import_plugin.h
+++ b/tools/editor/io_plugins/editor_sample_import_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -43,6 +43,8 @@ class EditorSampleImportPlugin : public EditorImportPlugin {
void _compress_ima_adpcm(const Vector<float>& p_data,DVector<uint8_t>& dst_data);
public:
+ static EditorSampleImportPlugin *singleton;
+
virtual String get_name() const;
virtual String get_visible_name() const;
virtual void import_dialog(const String& p_from="");
@@ -52,4 +54,16 @@ public:
EditorSampleImportPlugin(EditorNode* p_editor);
};
+class EditorSampleExportPlugin : public EditorExportPlugin {
+
+ OBJ_TYPE( EditorSampleExportPlugin, EditorExportPlugin);
+
+
+public:
+
+ virtual Vector<uint8_t> custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform);
+
+ EditorSampleExportPlugin();
+};
+
#endif // EDITOR_SAMPLE_IMPORT_PLUGIN_H
diff --git a/tools/editor/io_plugins/editor_scene_import_plugin.cpp b/tools/editor/io_plugins/editor_scene_import_plugin.cpp
index 06780e4d8a..ca44df269b 100644
--- a/tools/editor/io_plugins/editor_scene_import_plugin.cpp
+++ b/tools/editor/io_plugins/editor_scene_import_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -162,8 +162,8 @@ class EditorSceneImportDialog : public ConfirmationDialog {
LineEdit *save_path;
LineEdit *script_path;
Tree *import_options;
- FileDialog *file_select;
- FileDialog *script_select;
+ EditorFileDialog *file_select;
+ EditorFileDialog *script_select;
EditorDirDialog *save_select;
OptionButton *texture_action;
@@ -671,15 +671,28 @@ void EditorSceneImportDialog::_import(bool p_and_open) {
wip_open=p_and_open;
//' ImportMonitorBlock imb;
- if (import_path->get_text()=="") {
+
+ if (import_path->get_text().strip_edges()=="") {
error_dialog->set_text("Source path is empty.");
- error_dialog->popup_centered(Size2(200,100));
+ error_dialog->popup_centered_minsize();
return;
}
- if (save_path->get_text()=="") {
+ if (save_path->get_text().strip_edges()=="") {
error_dialog->set_text("Target path is empty.");
- error_dialog->popup_centered(Size2(200,100));
+ error_dialog->popup_centered_minsize();
+ return;
+ }
+
+ if (!save_path->get_text().begins_with("res://")) {
+ error_dialog->set_text("Target path must be full resource path.");
+ error_dialog->popup_centered_minsize();
+ return;
+ }
+
+ if (!DirAccess::exists(save_path->get_text())) {
+ error_dialog->set_text("Target path must exist.");
+ error_dialog->popup_centered_minsize();
return;
}
@@ -701,7 +714,8 @@ void EditorSceneImportDialog::_import(bool p_and_open) {
}
- Ref<EditorScenePostImport> pi;
+
+
if (script_path->get_text()!="") {
Ref<Script> scr = ResourceLoader::load(script_path->get_text());
@@ -711,7 +725,7 @@ void EditorSceneImportDialog::_import(bool p_and_open) {
return;
}
- pi = Ref<EditorScenePostImport>( memnew( EditorScenePostImport ) );
+ Ref<EditorScenePostImport> pi = Ref<EditorScenePostImport>( memnew( EditorScenePostImport ) );
pi->set_script(scr.get_ref_ptr());
if (!pi->get_script_instance()) {
@@ -719,6 +733,7 @@ void EditorSceneImportDialog::_import(bool p_and_open) {
error_dialog->popup_centered(Size2(200,100));
return;
}
+
}
@@ -747,7 +762,7 @@ void EditorSceneImportDialog::_import(bool p_and_open) {
rim->set_option("animation_optimizer_max_angle",animation_options->get_optimize_max_angle());
rim->set_option("animation_filters",animation_options->get_filter());
rim->set_option("animation_clips",animation_options->get_clips());
- rim->set_option("post_import_script",script_path->get_text()!=String()?EditorImportPlugin::validate_source_path(script_path->get_text()):String());
+ rim->set_option("post_import_script",script_path->get_text());
rim->set_option("import_this_time",this_import->get_selected());
rim->set_option("import_next_time",next_import->get_selected());
rim->set_option("reimport",true);
@@ -893,6 +908,7 @@ void EditorSceneImportDialog::popup_import(const String &p_from) {
if (rimd->has_option("animation_optimizer_max_angle"))
animation_options->set_optimize_max_angle(rimd->get_option("animation_optimizer_max_angle"));
+
script_path->set_text(rimd->get_option("post_import_script"));
if (rimd->has_option("import_this_time"))
this_import->select(rimd->get_option("import_this_time"));
@@ -1063,19 +1079,19 @@ EditorSceneImportDialog::EditorSceneImportDialog(EditorNode *p_editor, EditorSce
vbc->set_v_size_flags(SIZE_EXPAND_FILL);
vbc->add_margin_child("Options:",import_options,true);
- file_select = memnew(FileDialog);
- file_select->set_access(FileDialog::ACCESS_FILESYSTEM);
+ file_select = memnew(EditorFileDialog);
+ file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
add_child(file_select);
- file_select->set_mode(FileDialog::MODE_OPEN_FILE);
+ file_select->set_mode(EditorFileDialog::MODE_OPEN_FILE);
file_select->connect("file_selected", this,"_choose_file");
save_select = memnew(EditorDirDialog);
add_child(save_select);
- //save_select->set_mode(FileDialog::MODE_SAVE_FILE);
+ //save_select->set_mode(EditorFileDialog::MODE_SAVE_FILE);
save_select->connect("dir_selected", this,"_choose_save_file");
get_ok()->connect("pressed", this,"_import");
@@ -1124,7 +1140,7 @@ EditorSceneImportDialog::EditorSceneImportDialog(EditorNode *p_editor, EditorSce
script_choose->connect("pressed", this,"_browse_script");
- script_select = memnew(FileDialog);
+ script_select = memnew(EditorFileDialog);
add_child(script_select);
for(int i=0;i<ScriptServer::get_language_count();i++) {
@@ -1136,7 +1152,7 @@ EditorSceneImportDialog::EditorSceneImportDialog(EditorNode *p_editor, EditorSce
}
- script_select->set_mode(FileDialog::MODE_OPEN_FILE);
+ script_select->set_mode(EditorFileDialog::MODE_OPEN_FILE);
script_select->connect("file_selected", this,"_choose_script");
@@ -1798,8 +1814,8 @@ Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh>
for(int i=0;i<portal_points.size()-1;i++) {
- float a = portal_points[i].atan2();
- float b = portal_points[i+1].atan2();
+ float a = portal_points[i].angle();
+ float b = portal_points[i+1].angle();
if (a>b) {
SWAP( portal_points[i], portal_points[i+1] );
@@ -2121,7 +2137,7 @@ void EditorSceneImportPlugin::_merge_existing_node(Node *p_node,Node *p_imported
}
-void EditorSceneImportPlugin::_add_new_nodes(Node *p_node,Node *p_imported,Node *p_imported_scene,Set<Node*> &checked_nodes) {
+void EditorSceneImportPlugin::_add_new_nodes(Node *p_node,Node *p_imported,Node *p_imported_scene,Node *p_existing_scene,Set<Node*> &checked_nodes) {
for(int i=0;i<p_imported->get_child_count();i++) {
@@ -2129,12 +2145,15 @@ void EditorSceneImportPlugin::_add_new_nodes(Node *p_node,Node *p_imported,Node
Node *imported_node = p_imported->get_child(i);
- if (imported_node->get_owner()!=p_imported_scene)
+ if (imported_node->get_owner()!=p_imported_scene) {
+ // print_line("skipping because not imported at "+String(imported_node->get_name()));
continue; //end of the road
+ }
Vector<StringName> nn;
nn.push_back(imported_node->get_name());
NodePath imported_path(nn,false);
+ //print_line("check for: "+String(imported_path));
if (!p_node->has_node(imported_path) && !checked_nodes.has(imported_node)) {
//not there, re-add it
@@ -2144,8 +2163,11 @@ void EditorSceneImportPlugin::_add_new_nodes(Node *p_node,Node *p_imported,Node
if (o)
n=o->cast_to<Node>();
+ //print_line("creating node of same type..");
+
if (n) {
+ //print_line("copy props and add");
List<PropertyInfo> pl;
imported_node->get_property_list(&pl);
for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
@@ -2155,8 +2177,11 @@ void EditorSceneImportPlugin::_add_new_nodes(Node *p_node,Node *p_imported,Node
}
p_node->add_child(n);
+ n->set_owner(p_existing_scene);
}
+ } else {
+ //print_line("already exists");
}
@@ -2164,7 +2189,7 @@ void EditorSceneImportPlugin::_add_new_nodes(Node *p_node,Node *p_imported,Node
Node *other_node = p_node->get_node(imported_path);
- _add_new_nodes(other_node,imported_node,p_imported_scene,checked_nodes);
+ _add_new_nodes(other_node,imported_node,p_imported_scene,p_existing_scene,checked_nodes);
}
@@ -2177,7 +2202,7 @@ void EditorSceneImportPlugin::_merge_scenes(Node *p_node,Node *p_imported) {
Set<Ref<Resource> > checked_resources;
Set<Node*> checked_nodes;
_merge_existing_node(p_node,p_imported,checked_resources,checked_nodes);
- _add_new_nodes(p_node,p_imported,p_imported,checked_nodes);
+ _add_new_nodes(p_node,p_imported,p_imported,p_node,checked_nodes);
//add existing.. ?
}
@@ -2214,27 +2239,33 @@ void EditorSceneImportPlugin::_scan_materials(Node*p_base,Node *p_node,Map<Strin
void EditorSceneImportPlugin::_apply_materials(Node*p_base,Node *p_node,Map<String,Ref<Material> > &mesh_materials,Map<String,Ref<Material> >& override_materials,Set<Ref<Mesh> >& meshes_processed) {
- if (!p_base && p_node->get_owner()!=p_base)
+ if (p_node!=p_base && p_node->get_owner()!=p_base)
return;
MeshInstance *mi=p_node->cast_to<MeshInstance>();
if (mi) {
+ print_line("is mesh "+String(p_node->get_name()));
String path = p_base->get_path_to(p_node);
- if (override_materials.has(path))
+ if (override_materials.has(path)) {
+ print_line("is in material overrides");
mi->set_material_override(override_materials[path]);
+ }
Ref<Mesh> mesh = mi->get_mesh();
if (mesh.is_valid() && !meshes_processed.has(mesh)) {
+ print_line("mesh was not processed");
meshes_processed.insert(mesh);
for(int i=0;i<mesh->get_surface_count();i++) {
String name = mesh->get_name()+":"+mesh->surface_get_name(i);
+ print_line("name for surface "+itos(i)+": "+name);
if (mesh_materials.has(name)) {
Ref<Material> mat = mesh_materials[name];
mesh->surface_set_material(i,mat);
+ print_line("overriding!");
}
}
}
@@ -2251,9 +2282,19 @@ void EditorSceneImportPlugin::_merge_materials(Node *p_node,Node *p_imported) {
Map<String,Ref<Material> > override_materials;
_scan_materials(p_node,p_node,mesh_materials,override_materials);
+
+ for (Map<String,Ref<Material> >::Element *E=mesh_materials.front();E;E=E->next()) {
+ print_line("Mats: "+String(E->key()));
+ }
+
+ for (Map<String,Ref<Material> >::Element *E=override_materials.front();E;E=E->next()) {
+ print_line("Overrides: "+String(E->key()));
+ }
+
Set<Ref<Mesh> > mp;
_apply_materials(p_imported,p_imported,mesh_materials,override_materials,mp);
+
}
#if 0
@@ -2597,8 +2638,11 @@ void EditorSceneImportPlugin::_filter_tracks(Node *scene, const String& p_text)
for(Set<String>::Element *F=keep_local.front();F;F=F->next()) {
keep.insert(F->get());
}
-
+ print_line("FILTERING ANIM: "+String(E->get()));
_filter_anim_tracks(anim->get_animation(name),keep);
+ } else {
+ print_line("NOT FILTERING ANIM: "+String(E->get()));
+
}
}
@@ -2687,7 +2731,7 @@ Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, c
Ref<EditorScenePostImport> post_import_script;
if (post_import_script_path!="") {
- post_import_script_path = EditorImportPlugin::expand_source_path(post_import_script_path);
+ post_import_script_path = post_import_script_path;
Ref<Script> scr = ResourceLoader::load(post_import_script_path);
if (!scr.is_valid()) {
EditorNode::add_io_error("Couldn't load post-import script: '"+post_import_script_path);
@@ -2709,8 +2753,11 @@ Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, c
EditorNode::add_io_error("Error running Post-Import script: '"+post_import_script_path);
return err;
}
+
+
}
+
/// IMPORT IMAGES
diff --git a/tools/editor/io_plugins/editor_scene_import_plugin.h b/tools/editor/io_plugins/editor_scene_import_plugin.h
index fa4730f7ee..71efab9503 100644
--- a/tools/editor/io_plugins/editor_scene_import_plugin.h
+++ b/tools/editor/io_plugins/editor_scene_import_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -113,7 +113,7 @@ class EditorSceneImportPlugin : public EditorImportPlugin {
void _filter_tracks(Node *scene, const String& p_text);
void _merge_existing_node(Node *p_node,Node *p_imported_scene,Set<Ref<Resource> >& checked_resources,Set<Node*> &checked_nodes);
- void _add_new_nodes(Node *p_node,Node *p_imported,Node *p_imported_scene,Set<Node*> &checked_nodes);
+ void _add_new_nodes(Node *p_node,Node *p_imported,Node *p_imported_scene,Node *p_existing_scene,Set<Node*> &checked_nodes);
void _optimize_animations(Node *scene, float p_max_lin_error,float p_max_ang_error,float p_max_angle);
void _merge_scenes(Node *p_node, Node *p_imported);
diff --git a/tools/editor/io_plugins/editor_texture_import_plugin.cpp b/tools/editor/io_plugins/editor_texture_import_plugin.cpp
index b855b15b39..8d5a4f1dcf 100644
--- a/tools/editor/io_plugins/editor_texture_import_plugin.cpp
+++ b/tools/editor/io_plugins/editor_texture_import_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -51,6 +51,7 @@ static const char *flag_names[]={
NULL
};
+#if 0 // not used
static const char *flag_short_names[]={
"Stream",
"FixBorder",
@@ -65,6 +66,7 @@ static const char *flag_short_names[]={
"Anisoropic",
NULL
};
+#endif
void EditorImportTextureOptions::set_format(EditorTextureImportPlugin::ImageFormat p_format) {
@@ -142,6 +144,8 @@ void EditorImportTextureOptions::_changed() {
void EditorImportTextureOptions::_bind_methods() {
+ print_line("bind toptions");
+
ObjectTypeDB::bind_method("_changed",&EditorImportTextureOptions::_changed);
ObjectTypeDB::bind_method("_changedp",&EditorImportTextureOptions::_changedp);
@@ -217,7 +221,6 @@ EditorImportTextureOptions::EditorImportTextureOptions() {
fname++;
}
-
add_margin_child("Texture Options",flags,true);
notice_for_2d = memnew( Label );
@@ -245,17 +248,20 @@ class EditorTextureImportDialog : public ConfirmationDialog {
LineEdit *import_path;
LineEdit *save_path;
- FileDialog *file_select;
- FileDialog *save_file_select;
+ EditorFileDialog *file_select;
+ EditorFileDialog *save_file_select;
EditorDirDialog *save_select;
OptionButton *texture_action;
ConfirmationDialog *error_dialog;
CheckButton *crop_source;
+ SpinBox *size;
bool atlas;
+ bool large;
EditorTextureImportPlugin *plugin;
void _choose_files(const Vector<String>& p_path);
+ void _choose_file(const String& p_path);
void _choose_save_dir(const String& p_path);
void _browse();
void _browse_target();
@@ -270,7 +276,7 @@ public:
Error import(const String& p_from, const String& p_to, const String& p_preset);
void popup_import(const String &p_from=String());
- EditorTextureImportDialog(EditorTextureImportPlugin *p_plugin=NULL,bool p_2d=false,bool p_atlas=false);
+ EditorTextureImportDialog(EditorTextureImportPlugin *p_plugin=NULL,bool p_2d=false,bool p_atlas=false,bool p_large=false);
};
@@ -299,6 +305,15 @@ void EditorTextureImportDialog::_choose_files(const Vector<String>& p_path) {
import_path->set_text(files);
}
+
+
+
+void EditorTextureImportDialog::_choose_file(const String& p_path) {
+
+
+ import_path->set_text(p_path);
+
+}
void EditorTextureImportDialog::_choose_save_dir(const String& p_path) {
save_path->set_text(p_path);
@@ -321,12 +336,23 @@ void EditorTextureImportDialog::_import() {
String dst_path=save_path->get_text();
- if (dst_path.empty()) {
+ if (save_path->get_text().strip_edges()=="") {
+ error_dialog->set_text("Target path is empty.");
+ error_dialog->popup_centered_minsize();
+ return;
+ }
- error_dialog->set_text("Please specify a valid target import path!");
- error_dialog->popup_centered(Size2(200,100));
+ if (!save_path->get_text().begins_with("res://")) {
+ error_dialog->set_text("Target path must be full resource path.");
+ error_dialog->popup_centered_minsize();
return;
+ }
+
+ if (!atlas && !large && !DirAccess::exists(save_path->get_text())) {
+ error_dialog->set_text("Target path must exist.");
+ error_dialog->popup_centered_minsize();
+ return;
}
if (atlas) { //atlas
@@ -349,6 +375,8 @@ void EditorTextureImportDialog::_import() {
imd->set_option("flags",texture_options->get_flags());
imd->set_option("quality",texture_options->get_quality());
imd->set_option("atlas",true);
+ imd->set_option("atlas_size",int(size->get_val()));
+ imd->set_option("large",false);
imd->set_option("crop",crop_source->is_pressed());
Error err = plugin->import(dst_file,imd);
@@ -359,7 +387,38 @@ void EditorTextureImportDialog::_import() {
return;
}
+ } else if (large) { //atlas
+
+ if (files.size()!=1) {
+
+ error_dialog->set_text("Only one file is required for large texture");
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+
+ }
+ String dst_file = dst_path;
+ //dst_file=dst_file.basename()+".tex";
+ Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata );
+ //imd->set_editor();
+ for(int i=0;i<files.size();i++) {
+ imd->add_source(EditorImportPlugin::validate_source_path(files[i]));
+ }
+ imd->set_option("format",texture_options->get_format());
+ imd->set_option("flags",texture_options->get_flags());
+ imd->set_option("quality",texture_options->get_quality());
+ imd->set_option("atlas",false);
+ imd->set_option("large",true);
+ imd->set_option("large_cell_size",int(size->get_val()));
+ imd->set_option("crop",crop_source->is_pressed());
+ Error err = plugin->import(dst_file,imd);
+ if (err) {
+
+ error_dialog->set_text("Error importing: "+dst_file.get_file());
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+
+ }
} else {
@@ -374,6 +433,8 @@ void EditorTextureImportDialog::_import() {
imd->set_option("flags",texture_options->get_flags());
imd->set_option("quality",texture_options->get_quality());
imd->set_option("atlas",false);
+ imd->set_option("large",false);
+
Error err = plugin->import(dst_file,imd);
if (err) {
@@ -395,7 +456,7 @@ void EditorTextureImportDialog::_browse() {
void EditorTextureImportDialog::_browse_target() {
- if (atlas) {
+ if (atlas || large) {
save_file_select->popup_centered_ratio();
} else {
save_select->popup_centered_ratio();
@@ -411,7 +472,11 @@ void EditorTextureImportDialog::popup_import(const String& p_from) {
Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_from);
ERR_FAIL_COND(!rimd.is_valid());
- save_path->set_text(p_from.get_base_dir());
+ if (plugin->get_mode()==EditorTextureImportPlugin::MODE_ATLAS || plugin->get_mode()==EditorTextureImportPlugin::MODE_LARGE)
+ save_path->set_text(p_from);
+ else
+ save_path->set_text(p_from.get_base_dir());
+
texture_options->set_format(EditorTextureImportPlugin::ImageFormat(int(rimd->get_option("format"))));
texture_options->set_flags(rimd->get_option("flags"));
texture_options->set_quality(rimd->get_option("quality"));
@@ -457,6 +522,7 @@ void EditorTextureImportDialog::_bind_methods() {
ObjectTypeDB::bind_method("_choose_files",&EditorTextureImportDialog::_choose_files);
+ ObjectTypeDB::bind_method("_choose_file",&EditorTextureImportDialog::_choose_file);
ObjectTypeDB::bind_method("_choose_save_dir",&EditorTextureImportDialog::_choose_save_dir);
ObjectTypeDB::bind_method("_import",&EditorTextureImportDialog::_import);
ObjectTypeDB::bind_method("_browse",&EditorTextureImportDialog::_browse);
@@ -464,21 +530,25 @@ void EditorTextureImportDialog::_bind_methods() {
// ADD_SIGNAL( MethodInfo("imported",PropertyInfo(Variant::OBJECT,"scene")) );
}
-EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin* p_plugin, bool p_2d, bool p_atlas) {
+EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin* p_plugin, bool p_2d, bool p_atlas,bool p_large) {
atlas=p_atlas;
+ large=p_large;
plugin=p_plugin;
set_title("Import Textures");
- texture_options = memnew( EditorImportTextureOptions );;
- VBoxContainer *vbc = texture_options;
+
+ VBoxContainer *vbc = memnew(VBoxContainer);
add_child(vbc);
set_child_rect(vbc);
VBoxContainer *source_vb=memnew(VBoxContainer);
- vbc->add_margin_child("Source Texture(s):",source_vb);
+ if (large)
+ vbc->add_margin_child("Source Texture:",source_vb);
+ else
+ vbc->add_margin_child("Source Texture(s):",source_vb);
HBoxContainer *hbc = memnew( HBoxContainer );
source_vb->add_child(hbc);
@@ -493,6 +563,7 @@ EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin*
if (!p_atlas)
crop_source->hide();
+
Button * import_choose = memnew( Button );
import_choose->set_text(" .. ");
hbc->add_child(import_choose);
@@ -502,6 +573,19 @@ EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin*
hbc = memnew( HBoxContainer );
vbc->add_margin_child("Target Path:",hbc);
+ size = memnew( SpinBox );
+ size->set_min(128);
+ size->set_max(16384);
+
+ if (p_atlas) {
+ size->set_val(2048);
+ vbc->add_margin_child("Max Texture size:",size);
+ } else {
+ size->set_val(256);
+ vbc->add_margin_child("Cell Size:",size);
+ }
+
+
save_path = memnew( LineEdit );
save_path->set_h_size_flags(SIZE_EXPAND_FILL);
hbc->add_child(save_path);
@@ -512,32 +596,39 @@ EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin*
save_choose->connect("pressed", this,"_browse_target");
- file_select = memnew(FileDialog);
- file_select->set_access(FileDialog::ACCESS_FILESYSTEM);
+ file_select = memnew(EditorFileDialog);
+ file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
add_child(file_select);
- file_select->set_mode(FileDialog::MODE_OPEN_FILES);
+ if (!large)
+ file_select->set_mode(EditorFileDialog::MODE_OPEN_FILES);
+ else
+ file_select->set_mode(EditorFileDialog::MODE_OPEN_FILE);
file_select->connect("files_selected", this,"_choose_files");
+ file_select->connect("file_selected", this,"_choose_file");
- save_file_select = memnew(FileDialog);
- save_file_select->set_access(FileDialog::ACCESS_RESOURCES);
+ save_file_select = memnew(EditorFileDialog);
+ save_file_select->set_access(EditorFileDialog::ACCESS_RESOURCES);
add_child(save_file_select);
- save_file_select->set_mode(FileDialog::MODE_SAVE_FILE);
+ save_file_select->set_mode(EditorFileDialog::MODE_SAVE_FILE);
save_file_select->clear_filters();
- save_file_select->add_filter("*.tex;Base Atlas Texture");
+ if (large)
+ save_file_select->add_filter("*.ltex;Large Texture");
+ else
+ save_file_select->add_filter("*.tex;Base Atlas Texture");
save_file_select->connect("file_selected", this,"_choose_save_dir");
save_select = memnew( EditorDirDialog );
add_child(save_select);
-// save_select->set_mode(FileDialog::MODE_OPEN_DIR);
+// save_select->set_mode(EditorFileDialog::MODE_OPEN_DIR);
save_select->connect("dir_selected", this,"_choose_save_dir");
get_ok()->connect("pressed", this,"_import");
get_ok()->set_text("Import");
//move stuff up
- for(int i=0;i<4;i++)
- vbc->move_child( vbc->get_child( vbc->get_child_count() -1), 0);
+ //for(int i=0;i<4;i++)
+ // vbc->move_child( vbc->get_child( vbc->get_child_count() -1), 0);
error_dialog = memnew ( ConfirmationDialog );
add_child(error_dialog);
@@ -546,13 +637,24 @@ EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin*
set_hide_on_ok(false);
+ texture_options = memnew( EditorImportTextureOptions );;
+ vbc->add_child(texture_options);
+ texture_options->set_v_size_flags(SIZE_EXPAND_FILL);
+
if (atlas) {
texture_options->set_flags(EditorTextureImportPlugin::IMAGE_FLAG_FIX_BORDER_ALPHA|EditorTextureImportPlugin::IMAGE_FLAG_NO_MIPMAPS|EditorTextureImportPlugin::IMAGE_FLAG_FILTER);
texture_options->set_quality(0.7);
texture_options->set_format(EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSY);
- texture_options->show_2d_notice();
+ //texture_options->show_2d_notice();
set_title("Import Textures for Atlas (2D)");
+ } else if (large) {
+
+ texture_options->set_flags(EditorTextureImportPlugin::IMAGE_FLAG_FIX_BORDER_ALPHA|EditorTextureImportPlugin::IMAGE_FLAG_NO_MIPMAPS|EditorTextureImportPlugin::IMAGE_FLAG_FILTER);
+ texture_options->set_quality(0.7);
+ texture_options->set_format(EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS);
+ texture_options->show_2d_notice();
+ set_title("Import Large Textures (2D)");
} else if (p_2d) {
@@ -598,12 +700,17 @@ String EditorTextureImportPlugin::get_name() const {
return "texture_atlas";
} break;
+ case MODE_LARGE: {
+
+ return "texture_large";
+ } break;
}
return "";
}
+
String EditorTextureImportPlugin::get_visible_name() const {
switch(mode) {
@@ -618,7 +725,11 @@ String EditorTextureImportPlugin::get_visible_name() const {
} break;
case MODE_ATLAS: {
- return "Atlas Texture";
+ return "2D Atlas Texture";
+ } break;
+ case MODE_LARGE: {
+
+ return "2D Large Texture";
} break;
}
@@ -716,6 +827,135 @@ Error EditorTextureImportPlugin::import(const String& p_path, const Ref<Resource
return import2(p_path,p_from,EditorExportPlatform::IMAGE_COMPRESSION_BC,false);
}
+
+Error EditorTextureImportPlugin::_process_texture_data(Ref<ImageTexture> &texture,int format, float quality,int flags,EditorExportPlatform::ImageCompression p_compr,int tex_flags,float shrink) {
+
+
+ if (format==IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS || format==IMAGE_FORMAT_COMPRESS_DISK_LOSSY) {
+
+ Image image=texture->get_data();
+ ERR_FAIL_COND_V(image.empty(),ERR_INVALID_DATA);
+
+ bool has_alpha=image.detect_alpha();
+ if (!has_alpha && image.get_format()==Image::FORMAT_RGBA) {
+
+ image.convert(Image::FORMAT_RGB);
+
+ }
+
+ if (image.get_format()==Image::FORMAT_RGBA && flags&IMAGE_FLAG_FIX_BORDER_ALPHA) {
+
+ image.fix_alpha_edges();
+ }
+
+ if (image.get_format()==Image::FORMAT_RGBA && flags&IMAGE_FLAG_PREMULT_ALPHA) {
+
+ image.premultiply_alpha();
+ }
+
+ if (flags&IMAGE_FLAG_CONVERT_NORMAL_TO_XY) {
+ image.normalmap_to_xy();
+ }
+
+ //if ((image.get_format()==Image::FORMAT_RGB || image.get_format()==Image::FORMAT_RGBA) && flags&IMAGE_FLAG_CONVERT_TO_LINEAR) {
+
+ // image.srgb_to_linear();
+ //}
+
+ if (shrink>1) {
+
+ int orig_w=image.get_width();
+ int orig_h=image.get_height();
+ image.resize(orig_w/shrink,orig_h/shrink,Image::INTERPOLATE_CUBIC);
+ texture->create_from_image(image,tex_flags);
+ texture->set_size_override(Size2(orig_w,orig_h));
+
+
+ } else {
+
+ texture->create_from_image(image,tex_flags);
+ }
+
+
+ if (format==IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS) {
+ texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSLESS);
+ } else {
+ texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
+ }
+
+
+
+ texture->set_lossy_storage_quality(quality);
+
+
+ } else {
+
+
+ Image image=texture->get_data();
+ ERR_FAIL_COND_V(image.empty(),ERR_INVALID_DATA);
+
+
+ bool has_alpha=image.detect_alpha();
+ if (!has_alpha && image.get_format()==Image::FORMAT_RGBA) {
+
+ image.convert(Image::FORMAT_RGB);
+
+ }
+
+ if (image.get_format()==Image::FORMAT_RGBA && flags&IMAGE_FLAG_FIX_BORDER_ALPHA) {
+
+ image.fix_alpha_edges();
+ }
+
+ if (image.get_format()==Image::FORMAT_RGBA && flags&IMAGE_FLAG_PREMULT_ALPHA) {
+
+ image.premultiply_alpha();
+ }
+
+ if (flags&IMAGE_FLAG_CONVERT_NORMAL_TO_XY) {
+ image.normalmap_to_xy();
+ }
+
+ //if ((image.get_format()==Image::FORMAT_RGB || image.get_format()==Image::FORMAT_RGBA) && flags&IMAGE_FLAG_CONVERT_TO_LINEAR) {
+//
+ // print_line("CONVERT BECAUSE: "+itos(flags));
+ // image.srgb_to_linear();
+ //}
+
+ int orig_w=image.get_width();
+ int orig_h=image.get_height();
+
+ if (shrink>1) {
+ image.resize(orig_w/shrink,orig_h/shrink,Image::INTERPOLATE_CUBIC);
+ texture->create_from_image(image,tex_flags);
+ texture->set_size_override(Size2(orig_w,orig_h));
+ }
+
+ if (!(flags&IMAGE_FLAG_NO_MIPMAPS)) {
+ image.generate_mipmaps();
+
+ }
+
+ if (format!=IMAGE_FORMAT_UNCOMPRESSED) {
+
+ compress_image(p_compr,image,flags&IMAGE_FLAG_COMPRESS_EXTRA);
+ }
+
+
+ texture->create_from_image(image,tex_flags);
+
+
+ if (shrink>1 || (format!=IMAGE_FORMAT_UNCOMPRESSED && (image.get_width()!=orig_w || image.get_height()!=orig_h))) {
+ texture->set_size_override(Size2(orig_w,orig_h));
+ }
+
+ //uint32_t save_flags=ResourceSaver::FLAG_COMPRESS;
+ }
+
+ return OK;
+}
+
+
Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<ResourceImportMetadata>& p_from,EditorExportPlatform::ImageCompression p_compr, bool p_external){
@@ -727,8 +967,11 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc
Ref<ImageTexture> texture;
Vector<Ref<AtlasTexture> > atlases;
bool atlas = from->get_option("atlas");
+ bool large = from->get_option("large");
int flags=from->get_option("flags");
+ int format=from->get_option("format");
+ float quality=from->get_option("quality");
uint32_t tex_flags=0;
@@ -744,20 +987,95 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc
tex_flags|=Texture::FLAG_ANISOTROPIC_FILTER;
print_line("path: "+p_path+" flags: "+itos(tex_flags));
- int shrink=1;
+ float shrink=1;
if (from->has_option("shrink"))
shrink=from->get_option("shrink");
- if (atlas) {
+ if (large) {
+ ERR_FAIL_COND_V(from->get_source_count()!=1,ERR_INVALID_PARAMETER);
+
+ String src_path = EditorImportPlugin::expand_source_path(from->get_source_path(0));
+
+
+ int cell_size=from->get_option("large_cell_size");
+ ERR_FAIL_COND_V(cell_size<128 || cell_size>16384,ERR_CANT_OPEN);
+
+ EditorProgress pg("ltex","Import Large Texture",3);
+
+ pg.step("Load Source Image",0);
+ Image img;
+ Error err = ImageLoader::load_image(src_path,&img);
+ if (err) {
+ return err;
+ }
+
+ pg.step("Slicing",1);
+
+ Map<Vector2,Image> pieces;
+ for(int i=0;i<img.get_width();i+=cell_size) {
+ int w = MIN(img.get_width()-i,cell_size);
+ for(int j=0;j<img.get_height();j+=cell_size) {
+ int h = MIN(img.get_height()-j,cell_size);
+
+ Image piece(w,h,0,img.get_format());
+ piece.blit_rect(img,Rect2(i,j,w,h),Point2(0,0));
+ if (!piece.is_invisible()) {
+ pieces[Vector2(i,j)]=piece;
+ //print_line("ADDING PIECE AT "+Vector2(i,j));
+ }
+ }
+ }
+
+ Ref<LargeTexture> existing;
+ if (ResourceCache::has(p_path)) {
+ existing = ResourceCache::get(p_path);
+ }
+
+ if (existing.is_valid()) {
+ existing->clear();
+ } else {
+ existing = Ref<LargeTexture>(memnew( LargeTexture ));
+ }
+
+ existing->set_size(Size2(img.get_width(),img.get_height()));
+ pg.step("Inserting",2);
+
+ for (Map<Vector2,Image>::Element *E=pieces.front();E;E=E->next()) {
+
+ Ref<ImageTexture> imgtex = Ref<ImageTexture>( memnew( ImageTexture ) );
+ imgtex->create_from_image(E->get(),tex_flags);
+ _process_texture_data(imgtex,format,quality,flags,p_compr,tex_flags,shrink);
+ existing->add_piece(E->key(),imgtex);
+ }
+
+ if (!p_external) {
+ from->set_editor(get_name());
+ existing->set_path(p_path);
+ existing->set_import_metadata(from);
+ }
+ pg.step("Saving",3);
+
+ err = ResourceSaver::save(p_path,existing);
+ if (err!=OK) {
+ EditorNode::add_io_error("Couldn't save large texture: "+p_path);
+ return err;
+ }
+
+ return OK;
+
+
+ } else if (atlas) {
//prepare atlas!
Vector< Image > sources;
+ Vector< Image > tsources;
bool alpha=false;
bool crop = from->get_option("crop");
EditorProgress ep("make_atlas","Build Atlas For: "+p_path.get_file(),from->get_source_count()+3);
print_line("sources: "+itos(from->get_source_count()));
+
for(int i=0;i<from->get_source_count();i++) {
String path = EditorImportPlugin::expand_source_path(from->get_source_path(i));
@@ -775,17 +1093,57 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc
if (src.detect_alpha())
alpha=true;
- sources.push_back(src);
+ tsources.push_back(src);
}
ep.step("Converting Images",sources.size());
- for(int i=0;i<sources.size();i++) {
+ int base_index=0;
+
+
+ Map<uint64_t,int> source_md5;
+ Map<int,List<int> > source_map;
+
+ for(int i=0;i<tsources.size();i++) {
+
+ Image src = tsources[i];
if (alpha) {
- sources[i].convert(Image::FORMAT_RGBA);
+ src.convert(Image::FORMAT_RGBA);
+ } else {
+ src.convert(Image::FORMAT_RGB);
+ }
+
+ DVector<uint8_t> data = src.get_data();
+ MD5_CTX md5;
+ DVector<uint8_t>::Read r=data.read();
+ MD5Init(&md5);
+ int len=data.size();
+ for(int j=0;j<len;j++) {
+ uint8_t b = r[j];
+ b>>=2; //to aid in comparing
+ MD5Update(&md5,(unsigned char*)&b,1);
+ }
+ MD5Final(&md5);
+ uint64_t *cmp = (uint64_t*)md5.digest; //less bits, but still useful for this
+
+ tsources[i]=Image(); //clear
+
+ if (source_md5.has(*cmp)) {
+ int sidx=source_md5[*cmp];
+ source_map[sidx].push_back(i);
+ print_line("REUSING "+from->get_source_path(i));
+
} else {
- sources[i].convert(Image::FORMAT_RGB);
+ int sidx=sources.size();
+ source_md5[*cmp]=sidx;
+ sources.push_back(src);
+ List<int> sm;
+ sm.push_back(i);
+ source_map[sidx]=sm;
+
}
+
+
}
//texturepacker is not really good for optimizing, so..
@@ -822,31 +1180,59 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc
ep.step("Blitting Images",sources.size()+2);
+ bool blit_to_po2=tex_flags&Texture::FLAG_MIPMAPS;
+ int atlas_w=dst_size.width;
+ int atlas_h=dst_size.height;
+ if (blit_to_po2) {
+ atlas_w=nearest_power_of_2(dst_size.width);
+ atlas_h=nearest_power_of_2(dst_size.height);
+ }
Image atlas;
- atlas.create(nearest_power_of_2(dst_size.width),nearest_power_of_2(dst_size.height),0,alpha?Image::FORMAT_RGBA:Image::FORMAT_RGB);
+ atlas.create(atlas_w,atlas_h,0,alpha?Image::FORMAT_RGBA:Image::FORMAT_RGB);
+
+
+ atlases.resize(from->get_source_count());
for(int i=0;i<sources.size();i++) {
int x=dst_positions[i].x;
int y=dst_positions[i].y;
- Ref<AtlasTexture> at = memnew( AtlasTexture );
Size2 sz = Size2(sources[i].get_width(),sources[i].get_height());
+
+ Rect2 region;
+ Rect2 margin;
+
if (crop && sz!=crops[i].size) {
Rect2 rect = crops[i];
rect.size=sz-rect.size;
- at->set_region(Rect2(x+border,y+border,crops[i].size.width,crops[i].size.height));
- at->set_margin(rect);
+ region=Rect2(x+border,y+border,crops[i].size.width,crops[i].size.height);
+ margin=rect;
atlas.blit_rect(sources[i],crops[i],Point2(x+border,y+border));
} else {
- at->set_region(Rect2(x+border,y+border,sz.x,sz.y));
+ region=Rect2(x+border,y+border,sz.x,sz.y);
atlas.blit_rect(sources[i],Rect2(0,0,sources[i].get_width(),sources[i].get_height()),Point2(x+border,y+border));
}
- String apath = p_path.get_base_dir().plus_file(from->get_source_path(i).get_file().basename()+".atex");
- print_line("Atlas Tex: "+apath);
- at->set_path(apath);
- atlases.push_back(at);
+ ERR_CONTINUE( !source_map.has(i) );
+ for (List<int>::Element *E=source_map[i].front();E;E=E->next()) {
+
+ String apath = p_path.get_base_dir().plus_file(from->get_source_path(E->get()).get_file().basename()+".atex");
+
+ Ref<AtlasTexture> at;
+
+ if (ResourceCache::has(apath)) {
+ at = Ref<AtlasTexture>( ResourceCache::get(apath)->cast_to<AtlasTexture>() );
+ } else {
+
+ at = Ref<AtlasTexture>( memnew( AtlasTexture ) );
+ }
+ at->set_region(region);
+ at->set_margin(margin);
+ at->set_path(apath);
+ atlases[E->get()]=at;
+ print_line("Atlas Tex: "+apath);
+ }
}
if (ResourceCache::has(p_path)) {
texture = Ref<ImageTexture> ( ResourceCache::get(p_path)->cast_to<ImageTexture>() );
@@ -880,8 +1266,6 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc
}
- int format=from->get_option("format");
- float quality=from->get_option("quality");
if (!p_external) {
from->set_editor(get_name());
@@ -915,7 +1299,11 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc
}
}
+ bool compress=false;
+#if 1
+ _process_texture_data(texture,format,quality,flags,p_compr,tex_flags,shrink);
+#else
if (format==IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS || format==IMAGE_FORMAT_COMPRESS_DISK_LOSSY) {
Image image=texture->get_data();
@@ -972,13 +1360,6 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc
texture->set_lossy_storage_quality(quality);
- Error err = ResourceSaver::save(p_path,texture);
-
- if (err!=OK) {
- EditorNode::add_io_error("Couldn't save converted texture: "+p_path);
- return err;
- }
-
} else {
@@ -1041,15 +1422,20 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc
texture->set_size_override(Size2(orig_w,orig_h));
}
- uint32_t save_flags=ResourceSaver::FLAG_COMPRESS;
+ compress=true;
- Error err = ResourceSaver::save(p_path,texture,save_flags);
- if (err!=OK) {
- EditorNode::add_io_error("Couldn't save converted texture: "+p_path);
- return err;
- }
}
+#endif
+ uint32_t save_flags=0;
+ if (compress)
+ save_flags=ResourceSaver::FLAG_COMPRESS;
+
+ Error err = ResourceSaver::save(p_path,texture,save_flags);
+ if (err!=OK) {
+ EditorNode::add_io_error("Couldn't save converted texture: "+p_path);
+ return err;
+ }
return OK;
}
@@ -1098,6 +1484,9 @@ Vector<uint8_t> EditorTextureImportPlugin::custom_export(const String& p_path, c
case EditorImportExport::IMAGE_ACTION_COMPRESS_RAM: {
group_format=EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_RAM;
} break; //use default
+ case EditorImportExport::IMAGE_ACTION_KEEP: {
+ return Vector<uint8_t>();
+ } break; //use default
}
@@ -1238,14 +1627,14 @@ Vector<uint8_t> EditorTextureImportPlugin::custom_export(const String& p_path, c
}
-EditorTextureImportPlugin *EditorTextureImportPlugin::singleton[3]={NULL,NULL,NULL};
+EditorTextureImportPlugin *EditorTextureImportPlugin::singleton[EditorTextureImportPlugin::MODE_MAX]={NULL,NULL,NULL,NULL};
EditorTextureImportPlugin::EditorTextureImportPlugin(EditorNode *p_editor, Mode p_mode) {
singleton[p_mode]=this;
editor=p_editor;
- mode=p_mode;
- dialog = memnew( EditorTextureImportDialog(this,p_mode==MODE_TEXTURE_2D || p_mode==MODE_ATLAS,p_mode==MODE_ATLAS) );
+ mode=p_mode;
+ dialog = memnew( EditorTextureImportDialog(this,p_mode==MODE_TEXTURE_2D || p_mode==MODE_ATLAS || p_mode==MODE_LARGE,p_mode==MODE_ATLAS,p_mode==MODE_LARGE) );
editor->get_gui_base()->add_child(dialog);
}
diff --git a/tools/editor/io_plugins/editor_texture_import_plugin.h b/tools/editor/io_plugins/editor_texture_import_plugin.h
index d17b3c05c2..38fd671e9d 100644
--- a/tools/editor/io_plugins/editor_texture_import_plugin.h
+++ b/tools/editor/io_plugins/editor_texture_import_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -56,7 +56,9 @@ public:
enum Mode {
MODE_TEXTURE_2D,
MODE_TEXTURE_3D,
- MODE_ATLAS
+ MODE_ATLAS,
+ MODE_LARGE,
+ MODE_MAX
};
@@ -65,10 +67,10 @@ private:
Mode mode;
EditorNode *editor;
EditorTextureImportDialog *dialog;
- static EditorTextureImportPlugin *singleton[3];
+ static EditorTextureImportPlugin *singleton[MODE_MAX];
//used by other importers such as mesh
-
+ Error _process_texture_data(Ref<ImageTexture> &texture, int format, float quality, int flags,EditorExportPlatform::ImageCompression p_compr,int tex_flags,float shrink);
void compress_image(EditorExportPlatform::ImageCompression p_mode,Image& image,bool p_smaller);
public:
@@ -98,6 +100,7 @@ public:
IMAGE_FLAG_USE_ANISOTROPY=1024, //convert image to linear
};
+ Mode get_mode() const { return mode; }
virtual String get_name() const;
virtual String get_visible_name() const;
virtual void import_dialog(const String& p_from="");
@@ -120,6 +123,7 @@ public:
virtual Vector<uint8_t> custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform);
EditorTextureExportPlugin();
};
+
class EditorImportTextureOptions : public VBoxContainer {
OBJ_TYPE( EditorImportTextureOptions, VBoxContainer );
diff --git a/tools/editor/io_plugins/editor_translation_import_plugin.cpp b/tools/editor/io_plugins/editor_translation_import_plugin.cpp
index 9540869789..d152d71af4 100644
--- a/tools/editor/io_plugins/editor_translation_import_plugin.cpp
+++ b/tools/editor/io_plugins/editor_translation_import_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -47,7 +47,7 @@ class EditorTranslationImportDialog : public ConfirmationDialog {
LineEdit *import_path;
LineEdit *save_path;
- FileDialog *file_select;
+ EditorFileDialog *file_select;
CheckButton *ignore_first;
CheckButton *compress;
CheckButton *add_to_project;
@@ -347,16 +347,16 @@ public:
add_to_project->set_text("Add to Project (engine.cfg)");
tcomp->add_child(add_to_project);
- file_select = memnew(FileDialog);
- file_select->set_access(FileDialog::ACCESS_FILESYSTEM);
+ file_select = memnew(EditorFileDialog);
+ file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
add_child(file_select);
- file_select->set_mode(FileDialog::MODE_OPEN_FILE);
+ file_select->set_mode(EditorFileDialog::MODE_OPEN_FILE);
file_select->connect("file_selected", this,"_choose_file");
file_select->add_filter("*.csv ; Translation CSV");
save_select = memnew( EditorDirDialog );
add_child(save_select);
- // save_select->set_mode(FileDialog::MODE_OPEN_DIR);
+ // save_select->set_mode(EditorFileDialog::MODE_OPEN_DIR);
save_select->connect("dir_selected", this,"_choose_save_dir");
get_ok()->connect("pressed", this,"_import");
diff --git a/tools/editor/io_plugins/editor_translation_import_plugin.h b/tools/editor/io_plugins/editor_translation_import_plugin.h
index 8ea422c244..f3c2884534 100644
--- a/tools/editor/io_plugins/editor_translation_import_plugin.h
+++ b/tools/editor/io_plugins/editor_translation_import_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/multi_node_edit.cpp b/tools/editor/multi_node_edit.cpp
new file mode 100644
index 0000000000..cfa998bee9
--- /dev/null
+++ b/tools/editor/multi_node_edit.cpp
@@ -0,0 +1,118 @@
+#include "multi_node_edit.h"
+#include "editor_node.h"
+
+bool MultiNodeEdit::_set(const StringName& p_name, const Variant& p_value){
+
+ Node *es = EditorNode::get_singleton()->get_edited_scene();
+ if (!es)
+ return false;
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+
+ ur->create_action("MultiNode Set "+String(p_name));
+ for (const List<NodePath>::Element *E=nodes.front();E;E=E->next()) {
+
+ if (!es->has_node(E->get()))
+ continue;
+
+ Node*n=es->get_node(E->get());
+ if (!n)
+ continue;
+
+ ur->add_do_property(n,p_name,p_value);
+ ur->add_undo_property(n,p_name,n->get(p_name));
+
+ }
+
+ ur->commit_action();
+ return true;
+}
+
+bool MultiNodeEdit::_get(const StringName& p_name,Variant &r_ret) const {
+
+ Node *es = EditorNode::get_singleton()->get_edited_scene();
+ if (!es)
+ return false;
+
+ for (const List<NodePath>::Element *E=nodes.front();E;E=E->next()) {
+
+ if (!es->has_node(E->get()))
+ continue;
+
+ const Node*n=es->get_node(E->get());
+ if (!n)
+ continue;
+
+ bool found;
+ r_ret=n->get(p_name,&found);
+ if (found)
+ return true;
+
+ }
+
+ return false;
+}
+
+void MultiNodeEdit::_get_property_list( List<PropertyInfo> *p_list) const{
+
+ HashMap<String,PLData> usage;
+
+ Node *es = EditorNode::get_singleton()->get_edited_scene();
+ if (!es)
+ return;
+
+ int nc=0;
+
+ List<PLData*> datas;
+
+ for (const List<NodePath>::Element *E=nodes.front();E;E=E->next()) {
+
+ if (!es->has_node(E->get()))
+ continue;
+
+ Node*n=es->get_node(E->get());
+ if (!n)
+ continue;
+
+ List<PropertyInfo> plist;
+ n->get_property_list(&plist,true);
+
+ for(List<PropertyInfo>::Element *F=plist.front();F;F=F->next()) {
+
+ if (!usage.has(F->get().name)) {
+ PLData pld;
+ pld.uses=0;
+ pld.info=F->get();
+ usage[F->get().name]=pld;
+ datas.push_back(usage.getptr(F->get().name));
+ }
+
+ usage[F->get().name].uses++;
+ }
+
+ nc++;
+ }
+
+ for (List<PLData*>::Element *E=datas.front();E;E=E->next()) {
+
+ if (nc==E->get()->uses) {
+ p_list->push_back(E->get()->info);
+ }
+ }
+
+
+}
+
+void MultiNodeEdit::clear_nodes() {
+
+ nodes.clear();
+}
+
+void MultiNodeEdit::add_node(const NodePath& p_node){
+
+ nodes.push_back(p_node);
+}
+
+MultiNodeEdit::MultiNodeEdit()
+{
+}
diff --git a/tools/editor/multi_node_edit.h b/tools/editor/multi_node_edit.h
new file mode 100644
index 0000000000..5a0cabf4be
--- /dev/null
+++ b/tools/editor/multi_node_edit.h
@@ -0,0 +1,32 @@
+#ifndef MULTI_NODE_EDIT_H
+#define MULTI_NODE_EDIT_H
+
+#include "scene/main/node.h"
+
+class MultiNodeEdit : public Reference {
+
+ OBJ_TYPE(MultiNodeEdit,Reference);
+
+ List<NodePath> nodes;
+ struct PLData {
+ int uses;
+ PropertyInfo info;
+ };
+
+protected:
+
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+
+public:
+
+
+
+ void clear_nodes();
+ void add_node(const NodePath& p_node);
+
+ MultiNodeEdit();
+};
+
+#endif // MULTI_NODE_EDIT_H
diff --git a/tools/editor/optimized_save_dialog.cpp b/tools/editor/optimized_save_dialog.cpp
index 8a28272f12..687d3675fc 100644
--- a/tools/editor/optimized_save_dialog.cpp
+++ b/tools/editor/optimized_save_dialog.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/optimized_save_dialog.h b/tools/editor/optimized_save_dialog.h
index a3879b7cb4..739d0e1506 100644
--- a/tools/editor/optimized_save_dialog.h
+++ b/tools/editor/optimized_save_dialog.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/output_strings.cpp b/tools/editor/output_strings.cpp
index ec85505484..30569d11b0 100644
--- a/tools/editor/output_strings.cpp
+++ b/tools/editor/output_strings.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/output_strings.h b/tools/editor/output_strings.h
index cd9caa2b71..ad893534fa 100644
--- a/tools/editor/output_strings.h
+++ b/tools/editor/output_strings.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -26,64 +26,64 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef OUTPUT_STRINGS_H
-#define OUTPUT_STRINGS_H
-
-
-#include "scene/gui/control.h"
-#include "scene/gui/scroll_bar.h"
-#include "map.h"
-
-class OutputStrings : public Control {
-
- OBJ_TYPE( OutputStrings, Control );
-public:
-
- enum LineType {
-
- LINE_NORMAL,
- LINE_WARNING,
- LINE_ERROR,
- LINE_LINK
- };
-private:
-
- struct Line {
-
-
- LineType type;
- Variant meta;
- String text;
- };
-
-
- int font_height;
- int size_height;
-
- Size2 margin;
- typedef Map<int,Line> LineMap;
- Map<int,Line> line_map;
-
- VScrollBar *v_scroll;
- HScrollBar *h_scroll;
-
- bool following;
- int line_max_count;
- bool updating;
-
- void _vscroll_changed(float p_value);
- void _hscroll_changed(float p_value);
- void update_scrollbars();
-protected:
-
- static void _bind_methods();
- void _notification(int p_what);
-
-public:
-
- void add_line(const String& p_text, const Variant& p_meta=Variant(), const LineType p_type=LINE_NORMAL);
-
- OutputStrings();
-};
-
-#endif // OUTPUT_STRINGS_H
+#ifndef OUTPUT_STRINGS_H
+#define OUTPUT_STRINGS_H
+
+
+#include "scene/gui/control.h"
+#include "scene/gui/scroll_bar.h"
+#include "map.h"
+
+class OutputStrings : public Control {
+
+ OBJ_TYPE( OutputStrings, Control );
+public:
+
+ enum LineType {
+
+ LINE_NORMAL,
+ LINE_WARNING,
+ LINE_ERROR,
+ LINE_LINK
+ };
+private:
+
+ struct Line {
+
+
+ LineType type;
+ Variant meta;
+ String text;
+ };
+
+
+ int font_height;
+ int size_height;
+
+ Size2 margin;
+ typedef Map<int,Line> LineMap;
+ Map<int,Line> line_map;
+
+ VScrollBar *v_scroll;
+ HScrollBar *h_scroll;
+
+ bool following;
+ int line_max_count;
+ bool updating;
+
+ void _vscroll_changed(float p_value);
+ void _hscroll_changed(float p_value);
+ void update_scrollbars();
+protected:
+
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+
+ void add_line(const String& p_text, const Variant& p_meta=Variant(), const LineType p_type=LINE_NORMAL);
+
+ OutputStrings();
+};
+
+#endif // OUTPUT_STRINGS_H
diff --git a/tools/editor/pane_drag.cpp b/tools/editor/pane_drag.cpp
index f3a236201a..fb137de5ce 100644
--- a/tools/editor/pane_drag.cpp
+++ b/tools/editor/pane_drag.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/pane_drag.h b/tools/editor/pane_drag.h
index 268c940e35..a6cd9b6662 100644
--- a/tools/editor/pane_drag.h
+++ b/tools/editor/pane_drag.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -26,26 +26,26 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef PANE_DRAG_H
-#define PANE_DRAG_H
-
-#include "scene/gui/control.h"
-
-class PaneDrag : public Control {
-
- OBJ_TYPE( PaneDrag, Control );
-
- bool mouse_over;
-
-
-protected:
-
- void _input_event(const InputEvent& p_input);
- void _notification(int p_what);
- virtual Size2 get_minimum_size() const;
- static void _bind_methods();
-public:
- PaneDrag();
-};
-
-#endif // PANE_DRAG_H
+#ifndef PANE_DRAG_H
+#define PANE_DRAG_H
+
+#include "scene/gui/control.h"
+
+class PaneDrag : public Control {
+
+ OBJ_TYPE( PaneDrag, Control );
+
+ bool mouse_over;
+
+
+protected:
+
+ void _input_event(const InputEvent& p_input);
+ void _notification(int p_what);
+ virtual Size2 get_minimum_size() const;
+ static void _bind_methods();
+public:
+ PaneDrag();
+};
+
+#endif // PANE_DRAG_H
diff --git a/tools/editor/plugins/SCsub b/tools/editor/plugins/SCsub
index b525fb3f75..363a2ce4c0 100644
--- a/tools/editor/plugins/SCsub
+++ b/tools/editor/plugins/SCsub
@@ -1,7 +1,3 @@
Import('env')
Export('env')
env.add_source_files(env.tool_sources,"*.cpp")
-
-
-
-
diff --git a/tools/editor/plugins/animation_data_editor_plugin.cpp b/tools/editor/plugins/animation_data_editor_plugin.cpp
index 17f17bba7d..d8d65b875a 100644
--- a/tools/editor/plugins/animation_data_editor_plugin.cpp
+++ b/tools/editor/plugins/animation_data_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -26,8 +26,8 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "animation_data_editor_plugin.h"
-
-AnimationDataEditorPlugin::AnimationDataEditorPlugin()
-{
-}
+#include "animation_data_editor_plugin.h"
+
+AnimationDataEditorPlugin::AnimationDataEditorPlugin()
+{
+}
diff --git a/tools/editor/plugins/animation_data_editor_plugin.h b/tools/editor/plugins/animation_data_editor_plugin.h
index 2fd3d5b32a..0daa67d0a5 100644
--- a/tools/editor/plugins/animation_data_editor_plugin.h
+++ b/tools/editor/plugins/animation_data_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -26,13 +26,13 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef ANIMATION_DATA_EDITOR_PLUGIN_H
-#define ANIMATION_DATA_EDITOR_PLUGIN_H
-
-class AnimationDataEditorPlugin
-{
-public:
- AnimationDataEditorPlugin();
-};
-
-#endif // ANIMATION_DATA_EDITOR_PLUGIN_H
+#ifndef ANIMATION_DATA_EDITOR_PLUGIN_H
+#define ANIMATION_DATA_EDITOR_PLUGIN_H
+
+class AnimationDataEditorPlugin
+{
+public:
+ AnimationDataEditorPlugin();
+};
+
+#endif // ANIMATION_DATA_EDITOR_PLUGIN_H
diff --git a/tools/editor/plugins/animation_player_editor_plugin.cpp b/tools/editor/plugins/animation_player_editor_plugin.cpp
index 8bb37f1d71..6542fc8b4a 100644
--- a/tools/editor/plugins/animation_player_editor_plugin.cpp
+++ b/tools/editor/plugins/animation_player_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,8 +27,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "animation_player_editor_plugin.h"
+#include "globals.h"
#include "io/resource_loader.h"
-
+#include "io/resource_saver.h"
+#include "os/keyboard.h"
+#include "tools/editor/editor_settings.h"
void AnimationPlayerEditor::_node_removed(Node *p_node) {
@@ -76,6 +79,8 @@ void AnimationPlayerEditor::_notification(int p_what) {
seek->set_val(player->get_current_animation_pos());
if (edit_anim->is_pressed())
editor->get_animation_editor()->set_anim_pos(player->get_current_animation_pos());
+ EditorNode::get_singleton()->get_property_editor()->refresh();
+
} else if (last_active) {
//need the last frame after it stopped
@@ -95,15 +100,23 @@ void AnimationPlayerEditor::_notification(int p_what) {
duplicate_anim->set_icon( get_icon("Duplicate","EditorIcons") );
autoplay->set_icon( get_icon("AutoPlay","EditorIcons") );
load_anim->set_icon( get_icon("Folder","EditorIcons") );
- remove_anim->set_icon( get_icon("Del","EditorIcons") );
+ save_anim->set_icon(get_icon("Save", "EditorIcons"));
+ save_anim->get_popup()->connect("item_pressed", this, "_animation_save_menu");
+ remove_anim->set_icon( get_icon("Remove","EditorIcons") );
edit_anim->set_icon( get_icon("Edit","EditorIcons") );
blend_anim->set_icon( get_icon("Blend","EditorIcons") );
- play->set_icon( get_icon("Play","EditorIcons") );
+ play->set_icon( get_icon("PlayStart","EditorIcons") );
+ play_from->set_icon( get_icon("Play","EditorIcons") );
+ play_bw->set_icon( get_icon("PlayStartBackwards","EditorIcons") );
+ play_bw_from->set_icon( get_icon("PlayBackwards","EditorIcons") );
+
autoplay_icon=get_icon("AutoPlay","EditorIcons");
stop->set_icon( get_icon("Stop","EditorIcons") );
resource_edit_anim->set_icon( get_icon("EditResource","EditorIcons") );
pin->set_normal_texture(get_icon("Pin","EditorIcons") );
pin->set_pressed_texture( get_icon("PinPressed","EditorIcons") );
+ tool_anim->set_icon(get_icon("Tools","EditorIcons"));
+ tool_anim->get_popup()->connect("item_pressed",this,"_animation_tool_menu");
blend_editor.next->connect("text_changed",this,"_blend_editor_next_changed");
@@ -178,9 +191,82 @@ void AnimationPlayerEditor::_play_pressed() {
//unpause
//pause->set_pressed(false);
}
+
+void AnimationPlayerEditor::_play_from_pressed() {
+
+ String current;
+ if (animation->get_selected()>=0 && animation->get_selected()<animation->get_item_count()) {
+
+ current = animation->get_item_text( animation->get_selected() );
+ }
+
+ if (current!="") {
+
+ float time = player->get_current_animation_pos();
+
+ if (current==player->get_current_animation() && player->is_playing()) {
+
+ player->stop(); //so it wont blend with itself
+ }
+
+ player->play( current );
+ player->seek(time);
+ }
+
+ //unstop
+ stop->set_pressed(false);
+ //unpause
+ //pause->set_pressed(false);
+}
+
+
+void AnimationPlayerEditor::_play_bw_pressed() {
+
+ String current;
+ if (animation->get_selected()>=0 && animation->get_selected()<animation->get_item_count()) {
+
+ current = animation->get_item_text( animation->get_selected() );
+ }
+
+ if (current!="") {
+
+ if (current==player->get_current_animation())
+ player->stop(); //so it wont blend with itself
+ player->play(current,-1,-1,true);
+ }
+
+ //unstop
+ stop->set_pressed(false);
+ //unpause
+ //pause->set_pressed(false);
+}
+
+void AnimationPlayerEditor::_play_bw_from_pressed() {
+
+ String current;
+ if (animation->get_selected()>=0 && animation->get_selected()<animation->get_item_count()) {
+
+ current = animation->get_item_text( animation->get_selected() );
+ }
+
+ if (current!="") {
+
+ float time = player->get_current_animation_pos();
+ if (current==player->get_current_animation())
+ player->stop(); //so it wont blend with itself
+
+ player->play(current,-1,-1,true);
+ player->seek(time);
+ }
+
+ //unstop
+ stop->set_pressed(false);
+ //unpause
+ //pause->set_pressed(false);
+}
void AnimationPlayerEditor::_stop_pressed() {
- player->stop();
+ player->stop(false);
play->set_pressed(false);
stop->set_pressed(true);
//pause->set_pressed(false);
@@ -273,7 +359,7 @@ void AnimationPlayerEditor::_animation_rename() {
}
void AnimationPlayerEditor::_animation_load() {
ERR_FAIL_COND(!player);
- file->set_mode( FileDialog::MODE_OPEN_FILE );
+ file->set_mode( EditorFileDialog::MODE_OPEN_FILE );
file->clear_filters();
List<String> extensions;
@@ -285,8 +371,78 @@ void AnimationPlayerEditor::_animation_load() {
}
file->popup_centered_ratio();
+ current_option = RESOURCE_LOAD;
+}
+
+
+void AnimationPlayerEditor::_animation_save_in_path(const Ref<Resource>& p_resource, const String& p_path) {
+
+ int flg = 0;
+ if (EditorSettings::get_singleton()->get("on_save/compress_binary_resources"))
+ flg |= ResourceSaver::FLAG_COMPRESS;
+ if (EditorSettings::get_singleton()->get("on_save/save_paths_as_relative"))
+ flg |= ResourceSaver::FLAG_RELATIVE_PATHS;
+
+ String path = Globals::get_singleton()->localize_path(p_path);
+ Error err = ResourceSaver::save(path, p_resource, flg | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS);
+
+ if (err != OK) {
+ accept->set_text("Error saving resource!");
+ accept->popup_centered_minsize();
+ return;
+ }
+ // EditorFileSystem::get_singleton()->update_file(path,p_resource->get_type());
+
+ ((Resource*)p_resource.ptr())->set_path(path);
+ editor->emit_signal("resource_saved", p_resource);
+
+}
+void AnimationPlayerEditor::_animation_save(const Ref<Resource>& p_resource) {
+ if (p_resource->get_path().is_resource_file()) {
+ _animation_save_in_path(p_resource, p_resource->get_path());
+ }
+ else {
+ _animation_save_as(p_resource);
+ }
+}
+
+void AnimationPlayerEditor::_animation_save_as(const Ref<Resource>& p_resource) {
+
+ file->set_mode(EditorFileDialog::MODE_SAVE_FILE);
+ bool relpaths = (p_resource->has_meta("__editor_relpaths__") && p_resource->get_meta("__editor_relpaths__").operator bool());
+
+ List<String> extensions;
+ ResourceSaver::get_recognized_extensions(p_resource, &extensions);
+ file->clear_filters();
+ for (int i = 0; i<extensions.size(); i++) {
+
+ file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
+ }
+
+ //file->set_current_path(current_path);
+ if (p_resource->get_path() != "") {
+ file->set_current_path(p_resource->get_path());
+ if (extensions.size()) {
+ String ext = p_resource->get_path().extension().to_lower();
+ if (extensions.find(ext) == NULL) {
+ file->set_current_path(p_resource->get_path().replacen("." + ext, "." + extensions.front()->get()));
+ }
+ }
+ }
+ else {
+
+ String existing;
+ if (extensions.size()) {
+ existing = "new_" + p_resource->get_type().to_lower() + "." + extensions.front()->get().to_lower();
+ }
+ file->set_current_path(existing);
+
+ }
+ file->popup_centered_ratio();
+ file->set_title("Save Resource As..");
+ current_option = RESOURCE_SAVE;
}
void AnimationPlayerEditor::_animation_remove() {
@@ -334,7 +490,7 @@ void AnimationPlayerEditor::_animation_name_edited() {
String new_name = name->get_text();
if (new_name=="" || new_name.find(":")!=-1 || new_name.find("/")!=-1) {
error_dialog->set_text("ERROR: Invalid animation name!");
- error_dialog->popup_centered(Size2(300,70));
+ error_dialog->popup_centered_minsize();
return;
}
@@ -345,7 +501,7 @@ void AnimationPlayerEditor::_animation_name_edited() {
if (player->has_animation(new_name)) {
error_dialog->set_text("ERROR: Animation Name Already Exists!");
- error_dialog->popup_centered(Size2(300,70));
+ error_dialog->popup_centered_minsize();
return;
}
@@ -467,6 +623,49 @@ void AnimationPlayerEditor::ensure_visibility() {
_animation_edit();
}
+Dictionary AnimationPlayerEditor::get_state() const {
+
+ Dictionary d;
+
+ d["visible"]=is_visible();
+ if (is_visible() && player) {
+ d["player"]=EditorNode::get_singleton()->get_edited_scene()->get_path_to(player);
+ d["animation"]=player->get_current_animation();
+ d["editing"]=edit_anim->is_pressed();
+ }
+
+ return d;
+
+}
+void AnimationPlayerEditor::set_state(const Dictionary& p_state) {
+
+ if (p_state.has("visible") && p_state["visible"]) {
+
+ Node *n = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["player"]);
+ if (n && n->cast_to<AnimationPlayer>()) {
+ player=n->cast_to<AnimationPlayer>();
+ _update_player();
+ show();
+ set_process(true);
+ ensure_visibility();
+ EditorNode::get_singleton()->animation_panel_make_visible(true);
+
+ if (p_state.has("animation")) {
+ String anim = p_state["animation"];
+ _select_anim_by_name(anim);
+ if (p_state.has("editing") && p_state["editing"]) {
+
+ edit_anim->set_pressed(true);
+ _animation_edit();
+ }
+ }
+
+ }
+ }
+
+}
+
+
void AnimationPlayerEditor::_animation_resource_edit() {
if (animation->get_item_count()) {
@@ -510,38 +709,55 @@ void AnimationPlayerEditor::_animation_edit() {
//get_scene()->get_root_node()->call("_resource_selected",anim,"");
}
-void AnimationPlayerEditor::_file_selected(String p_file) {
+void AnimationPlayerEditor::_dialog_action(String p_file) {
- ERR_FAIL_COND(!player);
+ switch (current_option) {
+ case RESOURCE_LOAD: {
+ ERR_FAIL_COND(!player);
- Ref<Resource> res = ResourceLoader::load(p_file,"Animation");
- ERR_FAIL_COND(res.is_null());
- ERR_FAIL_COND( !res->is_type("Animation") );
- if (p_file.find_last("/")!=-1) {
+ Ref<Resource> res = ResourceLoader::load(p_file, "Animation");
+ ERR_FAIL_COND(res.is_null());
+ ERR_FAIL_COND(!res->is_type("Animation"));
+ if (p_file.find_last("/") != -1) {
- p_file=p_file.substr( p_file.find_last("/")+1, p_file.length() );
+ p_file = p_file.substr(p_file.find_last("/") + 1, p_file.length());
- }
- if (p_file.find_last("\\")!=-1) {
+ }
+ if (p_file.find_last("\\") != -1) {
- p_file=p_file.substr( p_file.find_last("\\")+1, p_file.length() );
+ p_file = p_file.substr(p_file.find_last("\\") + 1, p_file.length());
- }
+ }
- if (p_file.find(".")!=-1)
- p_file=p_file.substr(0,p_file.find("."));
+ if (p_file.find(".") != -1)
+ p_file = p_file.substr(0, p_file.find("."));
- undo_redo->create_action("Load Animation");
- undo_redo->add_do_method(player,"add_animation",p_file,res);
- undo_redo->add_undo_method(player,"remove_animation",p_file);
- if (player->has_animation(p_file)) {
- undo_redo->add_undo_method(player,"add_animation",p_file,player->get_animation(p_file));
+ undo_redo->create_action("Load Animation");
+ undo_redo->add_do_method(player, "add_animation", p_file, res);
+ undo_redo->add_undo_method(player, "remove_animation", p_file);
+ if (player->has_animation(p_file)) {
+ undo_redo->add_undo_method(player, "add_animation", p_file, player->get_animation(p_file));
- }
- undo_redo->add_do_method(this,"_animation_player_changed",player);
- undo_redo->add_undo_method(this,"_animation_player_changed",player);
- undo_redo->commit_action();
+ }
+ undo_redo->add_do_method(this, "_animation_player_changed", player);
+ undo_redo->add_undo_method(this, "_animation_player_changed", player);
+ undo_redo->commit_action();
+ break;
+ }
+ case RESOURCE_SAVE: {
+
+ String current = animation->get_item_text(animation->get_selected());
+ if (current != "") {
+ Ref<Animation> anim = player->get_animation(current);
+
+ ERR_FAIL_COND(!anim->cast_to<Resource>())
+
+ RES current_res = RES(anim->cast_to<Resource>());
+ _animation_save_in_path(current_res, p_file);
+ }
+ }
+ }
}
void AnimationPlayerEditor::_scale_changed(const String& p_scale) {
@@ -596,12 +812,17 @@ void AnimationPlayerEditor::_update_player() {
stop->set_disabled(animlist.size()==0);
play->set_disabled(animlist.size()==0);
+ play_bw->set_disabled(animlist.size()==0);
+ play_bw_from->set_disabled(animlist.size()==0);
+ play_from->set_disabled(animlist.size()==0);
autoplay->set_disabled(animlist.size()==0);
duplicate_anim->set_disabled(animlist.size()==0);
rename_anim->set_disabled(animlist.size()==0);
blend_anim->set_disabled(animlist.size()==0);
remove_anim->set_disabled(animlist.size()==0);
resource_edit_anim->set_disabled(animlist.size()==0);
+ save_anim->set_disabled(animlist.size() == 0);
+
int active_idx=-1;
for (List<StringName>::Element *E=animlist.front();E;E=E->next()) {
@@ -854,6 +1075,8 @@ void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos) {
return;
seek->set_val(p_pos);
+ EditorNode::get_singleton()->get_property_editor()->refresh();
+
//seekit
@@ -873,11 +1096,125 @@ void AnimationPlayerEditor::_hide_anim_editors() {
}
}
+
+void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
+
+ switch(p_option) {
+
+ case TOOL_COPY_ANIM: {
+
+ if (!animation->get_item_count()) {
+ error_dialog->set_text("ERROR: No animation to copy!");
+ error_dialog->popup_centered_minsize();
+ return;
+ }
+
+ String current = animation->get_item_text(animation->get_selected());
+ Ref<Animation> anim = player->get_animation(current);
+ //editor->edit_resource(anim);
+ EditorSettings::get_singleton()->set_resource_clipboard(anim);
+
+ } break;
+ case TOOL_PASTE_ANIM: {
+
+ Ref<Animation> anim = EditorSettings::get_singleton()->get_resource_clipboard();
+ if (!anim.is_valid()) {
+ error_dialog->set_text("ERROR: No animation resource on clipboard!");
+ error_dialog->popup_centered_minsize();
+ return;
+ }
+
+ String name = anim->get_name();
+ if (name=="") {
+ name="Pasted Animation";
+ }
+
+ int idx=1;
+ String base = name;
+ while (player->has_animation(name)) {
+
+ idx++;
+ name=base+" "+itos(idx);
+ }
+
+ undo_redo->create_action("Paste Animation");
+ undo_redo->add_do_method(player,"add_animation",name,anim);
+ undo_redo->add_undo_method(player,"remove_animation",name);
+ undo_redo->add_do_method(this,"_animation_player_changed",player);
+ undo_redo->add_undo_method(this,"_animation_player_changed",player);
+ undo_redo->commit_action();
+
+ _select_anim_by_name(name);
+
+
+ } break;
+ case TOOL_EDIT_RESOURCE: {
+
+ if (!animation->get_item_count()) {
+ error_dialog->set_text("ERROR: No animation to edit!");
+ error_dialog->popup_centered_minsize();
+ return;
+ }
+
+ String current = animation->get_item_text(animation->get_selected());
+ Ref<Animation> anim = player->get_animation(current);
+ editor->edit_resource(anim);
+
+ } break;
+
+ }
+}
+
+void AnimationPlayerEditor::_animation_save_menu(int p_option) {
+
+ String current = animation->get_item_text(animation->get_selected());
+ if (current != "") {
+ Ref<Animation> anim = player->get_animation(current);
+
+ switch (p_option) {
+ case ANIM_SAVE:
+ _animation_save(anim);
+ break;
+ case ANIM_SAVE_AS:
+ _animation_save_as(anim);
+ break;
+ }
+ }
+}
+
+void AnimationPlayerEditor::_unhandled_key_input(const InputEvent& p_ev) {
+
+ if (is_visible() && p_ev.type==InputEvent::KEY && p_ev.key.pressed && !p_ev.key.echo && !p_ev.key.mod.alt && !p_ev.key.mod.control && !p_ev.key.mod.meta) {
+
+ switch(p_ev.key.scancode) {
+
+ case KEY_A: {
+ if (!p_ev.key.mod.shift)
+ _play_bw_from_pressed();
+ else
+ _play_bw_pressed();
+ } break;
+ case KEY_S: {
+ _stop_pressed();
+ } break;
+ case KEY_D: {
+ if (!p_ev.key.mod.shift)
+ _play_from_pressed();
+ else
+ _play_pressed();
+ } break;
+ }
+ }
+}
+
void AnimationPlayerEditor::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_input_event"),&AnimationPlayerEditor::_input_event);
ObjectTypeDB::bind_method(_MD("_node_removed"),&AnimationPlayerEditor::_node_removed);
ObjectTypeDB::bind_method(_MD("_play_pressed"),&AnimationPlayerEditor::_play_pressed);
+ ObjectTypeDB::bind_method(_MD("_play_from_pressed"),&AnimationPlayerEditor::_play_from_pressed);
+ ObjectTypeDB::bind_method(_MD("_play_bw_pressed"),&AnimationPlayerEditor::_play_bw_pressed);
+ ObjectTypeDB::bind_method(_MD("_play_bw_from_pressed"),&AnimationPlayerEditor::_play_bw_from_pressed);
ObjectTypeDB::bind_method(_MD("_stop_pressed"),&AnimationPlayerEditor::_stop_pressed);
ObjectTypeDB::bind_method(_MD("_autoplay_pressed"),&AnimationPlayerEditor::_autoplay_pressed);
ObjectTypeDB::bind_method(_MD("_pause_pressed"),&AnimationPlayerEditor::_pause_pressed);
@@ -890,7 +1227,7 @@ void AnimationPlayerEditor::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_animation_blend"),&AnimationPlayerEditor::_animation_blend);
ObjectTypeDB::bind_method(_MD("_animation_edit"),&AnimationPlayerEditor::_animation_edit);
ObjectTypeDB::bind_method(_MD("_animation_resource_edit"),&AnimationPlayerEditor::_animation_resource_edit);
- ObjectTypeDB::bind_method(_MD("_file_selected"),&AnimationPlayerEditor::_file_selected);
+ ObjectTypeDB::bind_method(_MD("_dialog_action"),&AnimationPlayerEditor::_dialog_action);
ObjectTypeDB::bind_method(_MD("_seek_value_changed"),&AnimationPlayerEditor::_seek_value_changed);
ObjectTypeDB::bind_method(_MD("_animation_player_changed"),&AnimationPlayerEditor::_animation_player_changed);
ObjectTypeDB::bind_method(_MD("_blend_edited"),&AnimationPlayerEditor::_blend_edited);
@@ -904,6 +1241,9 @@ void AnimationPlayerEditor::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_hide_anim_editors"),&AnimationPlayerEditor::_hide_anim_editors);
ObjectTypeDB::bind_method(_MD("_animation_duplicate"),&AnimationPlayerEditor::_animation_duplicate);
ObjectTypeDB::bind_method(_MD("_blend_editor_next_changed"),&AnimationPlayerEditor::_blend_editor_next_changed);
+ ObjectTypeDB::bind_method(_MD("_unhandled_key_input"),&AnimationPlayerEditor::_unhandled_key_input);
+ ObjectTypeDB::bind_method(_MD("_animation_tool_menu"),&AnimationPlayerEditor::_animation_tool_menu);
+ ObjectTypeDB::bind_method(_MD("_animation_save_menu"), &AnimationPlayerEditor::_animation_save_menu);
@@ -931,47 +1271,67 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
add_child(hb);
- add_anim = memnew( Button );
+ add_anim = memnew( ToolButton );
add_anim->set_tooltip("Create new animation in player.");
hb->add_child(add_anim);
-
- load_anim = memnew( Button );
+ load_anim = memnew( ToolButton );
load_anim->set_tooltip("Load an animation from disk.");
hb->add_child(load_anim);
- duplicate_anim = memnew( Button );
+ save_anim = memnew(MenuButton);
+ save_anim->set_tooltip("Save the current animation");
+ save_anim->get_popup()->add_item("Save", ANIM_SAVE);
+ save_anim->get_popup()->add_item("Save As..", ANIM_SAVE_AS);
+ save_anim->set_focus_mode(Control::FOCUS_NONE);
+ hb->add_child(save_anim);
+
+ accept = memnew(AcceptDialog);
+ add_child(accept);
+ accept->connect("confirmed", this, "_menu_confirm_current");
+
+ duplicate_anim = memnew( ToolButton );
hb->add_child(duplicate_anim);
duplicate_anim->set_tooltip("Duplicate Animation");
+ rename_anim = memnew( ToolButton );
+ hb->add_child(rename_anim);
+ rename_anim->set_tooltip("Rename Animation");
+
+ remove_anim = memnew( ToolButton );
+
+ hb->add_child(remove_anim);
+ remove_anim->set_tooltip("Remove Animation");
+
+
animation = memnew( OptionButton );
hb->add_child(animation);
animation->set_h_size_flags(SIZE_EXPAND_FILL);
animation->set_tooltip("Display list of animations in player.");
- autoplay = memnew( Button );
+ autoplay = memnew( ToolButton );
hb->add_child(autoplay);
autoplay->set_tooltip("Autoplay On Load");
- rename_anim = memnew( Button );
- hb->add_child(rename_anim);
- rename_anim->set_tooltip("Rename Animation");
- remove_anim = memnew( Button );
-
- hb->add_child(remove_anim);
- remove_anim->set_tooltip("Remove Animation");
-
- blend_anim = memnew( Button );
+ blend_anim = memnew( ToolButton );
hb->add_child(blend_anim);
blend_anim->set_tooltip("Edit Target Blend Times");
+ tool_anim = memnew( MenuButton);
+ //tool_anim->set_flat(false);
+ tool_anim->set_tooltip("Animation Tools");
+ tool_anim->get_popup()->add_item("Copy Animation",TOOL_COPY_ANIM);
+ tool_anim->get_popup()->add_item("Paste Animation",TOOL_PASTE_ANIM);
+ //tool_anim->get_popup()->add_separator();
+ //tool_anim->get_popup()->add_item("Edit Anim Resource",TOOL_PASTE_ANIM);
+ hb->add_child(tool_anim);
- edit_anim = memnew( Button );
+ edit_anim = memnew( ToolButton );
edit_anim->set_toggle_mode(true);
hb->add_child(edit_anim);
edit_anim->set_tooltip("Open animation editor.\nProperty editor will displays all editable keys too.");
@@ -980,15 +1340,29 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
hb = memnew (HBoxContainer);
add_child(hb);
- play = memnew( Button );
- play->set_tooltip("Play selected animation.");
+ play_bw_from = memnew( ToolButton );
+ play_bw_from->set_tooltip("Play backwards selected animation from current pos. (A)");
+ hb->add_child(play_bw_from);
- hb->add_child(play);
+ play_bw = memnew( ToolButton );
+ play_bw->set_tooltip("Play backwards selected animation from end. (Shift+A)");
+ hb->add_child(play_bw);
- stop = memnew( Button );
+ stop = memnew( ToolButton );
stop->set_toggle_mode(true);
hb->add_child(stop);
- play->set_tooltip("Stop animation playback.");
+ stop->set_tooltip("Stop animation playback. (S)");
+
+ play = memnew( ToolButton );
+ play->set_tooltip("Play selected animation from start. (Shift+D)");
+ hb->add_child(play);
+
+
+ play_from = memnew( ToolButton );
+ play_from->set_tooltip("Play selected animation from current pos. (D)");
+ hb->add_child(play_from);
+
+
//pause = memnew( Button );
//pause->set_toggle_mode(true);
@@ -1020,12 +1394,14 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
resource_edit_anim= memnew( Button );
hb->add_child(resource_edit_anim);
+ resource_edit_anim->hide();
- file = memnew(FileDialog);
+ file = memnew(EditorFileDialog);
add_child(file);
name_dialog = memnew( ConfirmationDialog );
+ name_dialog->set_title("Create New Animation");
name_dialog->set_hide_on_ok(false);
add_child(name_dialog);
name = memnew( LineEdit );
@@ -1070,7 +1446,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
autoplay->connect("pressed", this,"_autoplay_pressed");
autoplay->set_toggle_mode(true);
- play->connect("pressed", this,"_play_pressed");
+ play->connect("pressed", this,"_play_pressed");
+ play_from->connect("pressed", this,"_play_from_pressed");
+ play_bw->connect("pressed", this,"_play_bw_pressed");
+ play_bw_from->connect("pressed", this,"_play_bw_from_pressed");
stop->connect("pressed", this,"_stop_pressed");
//pause->connect("pressed", this,"_pause_pressed");
add_anim->connect("pressed", this,"_animation_new");
@@ -1083,7 +1462,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
remove_anim->connect("pressed", this,"_animation_remove");
animation->connect("item_selected", this,"_animation_selected",Vector<Variant>(),true);
resource_edit_anim->connect("pressed", this,"_animation_resource_edit");
- file->connect("file_selected", this,"_file_selected");
+ file->connect("file_selected", this,"_dialog_action");
seek->connect("value_changed", this, "_seek_value_changed",Vector<Variant>(),true);
scale->connect("text_entered", this, "_scale_changed",Vector<Variant>(),true);
editor->get_animation_editor()->connect("timeline_changed",this,"_animation_key_editor_seek");
@@ -1100,6 +1479,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
renaming=false;
last_active=false;
+
+ set_process_unhandled_key_input(true);
}
@@ -1135,6 +1516,7 @@ AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin(EditorNode *p_node) {
editor=p_node;
anim_editor = memnew( AnimationPlayerEditor(editor) );
+ anim_editor->set_undo_redo(editor->get_undo_redo());
editor->get_animation_panel()->add_child(anim_editor);
/*
editor->get_viewport()->add_child(anim_editor);
diff --git a/tools/editor/plugins/animation_player_editor_plugin.h b/tools/editor/plugins/animation_player_editor_plugin.h
index 2c6bcae97e..ac4d1ab6ba 100644
--- a/tools/editor/plugins/animation_player_editor_plugin.h
+++ b/tools/editor/plugins/animation_player_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -49,10 +49,30 @@ class AnimationPlayerEditor : public VBoxContainer {
EditorNode *editor;
AnimationPlayer *player;
+ enum {
+ TOOL_COPY_ANIM,
+ TOOL_PASTE_ANIM,
+ TOOL_EDIT_RESOURCE
+ };
+
+ enum {
+ ANIM_SAVE,
+ ANIM_SAVE_AS
+ };
+
+ enum {
+ RESOURCE_LOAD,
+ RESOURCE_SAVE
+ };
+
OptionButton *animation;
Button *stop;
Button *play;
+ Button *play_from;
+ Button *play_bw;
+ Button *play_bw_from;
+
// Button *pause;
Button *add_anim;
Button *autoplay;
@@ -61,8 +81,10 @@ class AnimationPlayerEditor : public VBoxContainer {
Button *edit_anim;
Button *resource_edit_anim;
Button *load_anim;
+ MenuButton *save_anim;
Button *blend_anim;
Button *remove_anim;
+ MenuButton *tool_anim;
TextureButton *pin;
Label *nodename;
SpinBox *frame;
@@ -74,7 +96,9 @@ class AnimationPlayerEditor : public VBoxContainer {
Ref<Texture> autoplay_icon;
bool last_active;
- FileDialog *file;
+ EditorFileDialog *file;
+ AcceptDialog *accept;
+ int current_option;
struct BlendEditor {
@@ -95,6 +119,9 @@ class AnimationPlayerEditor : public VBoxContainer {
void _select_anim_by_name(const String& p_anim);
void _play_pressed();
+ void _play_from_pressed();
+ void _play_bw_pressed();
+ void _play_bw_from_pressed();
void _autoplay_pressed();
void _stop_pressed();
void _pause_pressed();
@@ -103,13 +130,18 @@ class AnimationPlayerEditor : public VBoxContainer {
void _animation_rename();
void _animation_name_edited();
void _animation_load();
+
+ void _animation_save_in_path(const Ref<Resource>& p_resource, const String& p_path);
+ void _animation_save(const Ref<Resource>& p_resource);
+ void _animation_save_as(const Ref<Resource>& p_resource);
+
void _animation_remove();
void _animation_blend();
void _animation_edit();
void _animation_duplicate();
void _animation_resource_edit();
void _scale_changed(const String& p_scale);
- void _file_selected(String p_file);
+ void _dialog_action(String p_file);
void _seek_frame_changed(const String& p_frame);
void _seek_value_changed(float p_value);
void _blend_editor_next_changed(const String& p_string);
@@ -126,6 +158,9 @@ class AnimationPlayerEditor : public VBoxContainer {
void _animation_key_editor_seek(float p_pos);
void _animation_key_editor_anim_len_changed(float p_new);
+ void _unhandled_key_input(const InputEvent& p_ev);
+ void _animation_tool_menu(int p_option);
+ void _animation_save_menu(int p_option);
AnimationPlayerEditor();
protected:
@@ -136,6 +171,10 @@ protected:
static void _bind_methods();
public:
+ Dictionary get_state() const;
+ void set_state(const Dictionary& p_state);
+
+
void ensure_visibility();
void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo=p_undo_redo; }
@@ -152,6 +191,9 @@ class AnimationPlayerEditorPlugin : public EditorPlugin {
public:
+ virtual Dictionary get_state() const { return anim_editor->get_state(); }
+ virtual void set_state(const Dictionary& p_state) { anim_editor->set_state(p_state); }
+
virtual String get_name() const { return "Anim"; }
bool has_main_screen() const { return false; }
virtual void edit(Object *p_node);
diff --git a/tools/editor/plugins/animation_tree_editor_plugin.cpp b/tools/editor/plugins/animation_tree_editor_plugin.cpp
index af15e17f50..382bc44726 100644
--- a/tools/editor/plugins/animation_tree_editor_plugin.cpp
+++ b/tools/editor/plugins/animation_tree_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -1193,7 +1193,7 @@ void AnimationTreeEditor::_add_menu_item(int p_item) {
} else if (p_item == MENU_IMPORT_ANIMATIONS) {
file_op = MENU_IMPORT_ANIMATIONS;
- file_dialog->set_mode(FileDialog::MODE_OPEN_FILE);
+ file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
file_dialog->popup_centered_ratio();
} else {
@@ -1458,7 +1458,7 @@ AnimationTreeEditor::AnimationTreeEditor() {
edit_check->hide();;
edit_check->connect("pressed", this,"_edit_dialog_changed");
- file_dialog = memnew( FileDialog );
+ file_dialog = memnew( EditorFileDialog );
file_dialog->set_enable_multiple_selection(true);
file_dialog->set_current_dir(Globals::get_singleton()->get_resource_path());
add_child(file_dialog);
diff --git a/tools/editor/plugins/animation_tree_editor_plugin.h b/tools/editor/plugins/animation_tree_editor_plugin.h
index 21b31863b6..bd29530c7a 100644
--- a/tools/editor/plugins/animation_tree_editor_plugin.h
+++ b/tools/editor/plugins/animation_tree_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -79,7 +79,7 @@ class AnimationTreeEditor : public Control {
Button *edit_button;
Button *filter_button;
CheckButton *edit_check;
- FileDialog* file_dialog;
+ EditorFileDialog* file_dialog;
int file_op;
void _popup_edit_dialog();
diff --git a/tools/editor/plugins/baked_light_baker.cpp b/tools/editor/plugins/baked_light_baker.cpp
index 42a185b7c2..4599dbfb54 100644
--- a/tools/editor/plugins/baked_light_baker.cpp
+++ b/tools/editor/plugins/baked_light_baker.cpp
@@ -1233,7 +1233,7 @@ float BakedLightBaker::_throw_ray(ThreadStack& thread_stack,bool p_bake_direct,c
if (dist<r) {
//avoid accumulaiton of light on corners
//plot_light=plot_light.linear_interpolate(Color(0,0,0,0),1.0-sd/plot_size*plot_size);
- skip-true;
+ skip=true;
} else {
@@ -2127,6 +2127,7 @@ void BakedLightBaker::_stop_thread() {
bake_thread_exit=true;
for(int i=0;i<threads.size();i++) {
Thread::wait_to_finish(threads[i]);
+ memdelete(threads[i]);
}
threads.clear();
}
diff --git a/tools/editor/plugins/baked_light_editor_plugin.cpp b/tools/editor/plugins/baked_light_editor_plugin.cpp
index 0f02899dc2..26524b2437 100644
--- a/tools/editor/plugins/baked_light_editor_plugin.cpp
+++ b/tools/editor/plugins/baked_light_editor_plugin.cpp
@@ -180,7 +180,7 @@ void BakedLightEditor::_bake_pressed() {
ERR_FAIL_COND(!node);
if (node->get_baked_light().is_null()) {
err_dialog->set_text("BakedLightInstance does not contain a BakedLight resource.");
- err_dialog->popup_centered(Size2(350,70));
+ err_dialog->popup_centered_minsize();
button_bake->set_pressed(false);
return;
}
@@ -242,7 +242,7 @@ void BakedLightEditor::_bake_lightmaps() {
if (err) {
err_dialog->set_text("Error baking to lightmaps!\nMake sure that a bake has just\n happened and that lightmaps are\n configured. ");
- err_dialog->popup_centered(Size2(350,70));
+ err_dialog->popup_centered_minsize();
return;
}
diff --git a/tools/editor/plugins/camera_editor_plugin.cpp b/tools/editor/plugins/camera_editor_plugin.cpp
index aa7562b17e..08ed2c745d 100644
--- a/tools/editor/plugins/camera_editor_plugin.cpp
+++ b/tools/editor/plugins/camera_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/plugins/camera_editor_plugin.h b/tools/editor/plugins/camera_editor_plugin.h
index 5529b32e56..afb8f9415d 100644
--- a/tools/editor/plugins/camera_editor_plugin.h
+++ b/tools/editor/plugins/camera_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/plugins/canvas_item_editor_plugin.cpp b/tools/editor/plugins/canvas_item_editor_plugin.cpp
index 599160eb46..e3f4edf967 100644
--- a/tools/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/tools/editor/plugins/canvas_item_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -36,18 +36,185 @@
#include "globals.h"
#include "os/input.h"
#include "tools/editor/editor_settings.h"
+#include "scene/gui/grid_container.h"
+
+class SnapDialog : public ConfirmationDialog {
+
+ OBJ_TYPE(SnapDialog,ConfirmationDialog);
+
+friend class CanvasItemEditor;
+
+ SpinBox *grid_offset_x;
+ SpinBox *grid_offset_y;
+ SpinBox *grid_step_x;
+ SpinBox *grid_step_y;
+ SpinBox *rotation_offset;
+ SpinBox *rotation_step;
+
+public:
+ SnapDialog() : ConfirmationDialog() {
+ const int SPIN_BOX_GRID_RANGE = 256;
+ const int SPIN_BOX_ROTATION_RANGE = 360;
+ Label *label;
+ VBoxContainer *container;
+ GridContainer *child_container;
+
+ set_title("Configure Snap");
+ get_ok()->set_text("Close");
+
+ container = memnew( VBoxContainer );
+ add_child(container);
+ set_child_rect(container);
+
+ child_container = memnew( GridContainer );
+ child_container->set_columns(3);
+ container->add_child(child_container);
+
+ label = memnew( Label );
+ label->set_text("Grid Offset:");
+ child_container->add_child(label);
+ label->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ grid_offset_x = memnew( SpinBox );
+ grid_offset_x->set_min(-SPIN_BOX_GRID_RANGE);
+ grid_offset_x->set_max(SPIN_BOX_GRID_RANGE);
+ grid_offset_x->set_suffix("px");
+ child_container->add_child(grid_offset_x);
+
+ grid_offset_y = memnew( SpinBox );
+ grid_offset_y->set_min(-SPIN_BOX_GRID_RANGE);
+ grid_offset_y->set_max(SPIN_BOX_GRID_RANGE);
+ grid_offset_y->set_suffix("px");
+ child_container->add_child(grid_offset_y);
+
+ label = memnew( Label );
+ label->set_text("Grid Step:");
+ child_container->add_child(label);
+ label->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ grid_step_x = memnew( SpinBox );
+ grid_step_x->set_min(-SPIN_BOX_GRID_RANGE);
+ grid_step_x->set_max(SPIN_BOX_GRID_RANGE);
+ grid_step_x->set_suffix("px");
+ child_container->add_child(grid_step_x);
+
+ grid_step_y = memnew( SpinBox );
+ grid_step_y->set_min(-SPIN_BOX_GRID_RANGE);
+ grid_step_y->set_max(SPIN_BOX_GRID_RANGE);
+ grid_step_y->set_suffix("px");
+ child_container->add_child(grid_step_y);
+
+ container->add_child( memnew( HSeparator ) );
+
+ child_container = memnew( GridContainer );
+ child_container->set_columns(2);
+ container->add_child(child_container);
+
+ label = memnew( Label );
+ label->set_text("Rotation Offset:");
+ child_container->add_child(label);
+ label->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ rotation_offset = memnew( SpinBox );
+ rotation_offset->set_min(-SPIN_BOX_ROTATION_RANGE);
+ rotation_offset->set_max(SPIN_BOX_ROTATION_RANGE);
+ rotation_offset->set_suffix("deg");
+ child_container->add_child(rotation_offset);
+
+ label = memnew( Label );
+ label->set_text("Rotation Step:");
+ child_container->add_child(label);
+ label->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ rotation_step = memnew( SpinBox );
+ rotation_step->set_min(-SPIN_BOX_ROTATION_RANGE);
+ rotation_step->set_max(SPIN_BOX_ROTATION_RANGE);
+ rotation_step->set_suffix("deg");
+ child_container->add_child(rotation_step);
+ }
+
+ void set_fields(const Point2 p_grid_offset, const Size2 p_grid_step, const float p_rotation_offset, const float p_rotation_step) {
+ grid_offset_x->set_val(p_grid_offset.x);
+ grid_offset_y->set_val(p_grid_offset.y);
+ grid_step_x->set_val(p_grid_step.x);
+ grid_step_y->set_val(p_grid_step.y);
+ rotation_offset->set_val(p_rotation_offset * (180 / Math_PI));
+ rotation_step->set_val(p_rotation_step * (180 / Math_PI));
+ }
+
+ void get_fields(Point2 &p_grid_offset, Size2 &p_grid_step, float &p_rotation_offset, float &p_rotation_step) {
+ p_grid_offset.x = grid_offset_x->get_val();
+ p_grid_offset.y = grid_offset_y->get_val();
+ p_grid_step.x = grid_step_x->get_val();
+ p_grid_step.y = grid_step_y->get_val();
+ p_rotation_offset = rotation_offset->get_val() / (180 / Math_PI);
+ p_rotation_step = rotation_step->get_val() / (180 / Math_PI);
+ }
+};
+
void CanvasItemEditor::_unhandled_key_input(const InputEvent& p_ev) {
if (!is_visible())
return;
+ if (p_ev.key.mod.control)
+ // prevent to change tool mode when control key is pressed
+ return;
if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_Q)
_tool_select(TOOL_SELECT);
if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_W)
_tool_select(TOOL_MOVE);
if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_E)
_tool_select(TOOL_ROTATE);
- if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_V && drag==DRAG_ALL && can_move_pivot)
- drag=DRAG_PIVOT;
+ if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_V && drag==DRAG_NONE && can_move_pivot) {
+ if (p_ev.key.mod.shift) {
+ //move drag pivot
+ drag=DRAG_PIVOT;
+ } else if (!Input::get_singleton()->is_mouse_button_pressed(0)) {
+
+ List<Node*> &selection = editor_selection->get_selected_node_list();
+
+ Vector2 mouse_pos = viewport->get_local_mouse_pos();
+ if (selection.size() && viewport->get_rect().has_point(mouse_pos)) {
+ //just in case, make it work if over viewport
+ mouse_pos=transform.affine_inverse().xform(mouse_pos);
+ mouse_pos=snap_point(mouse_pos);
+
+ undo_redo->create_action("Move Pivot");
+
+ for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
+
+ Node2D *n2d = E->get()->cast_to<Node2D>();
+
+ if (n2d && n2d->edit_has_pivot()) {
+
+ Vector2 offset = n2d->edit_get_pivot();
+ Vector2 gpos = n2d->get_global_pos();
+
+ Vector2 motion_ofs = gpos-mouse_pos;
+
+ undo_redo->add_do_method(n2d,"set_global_pos",mouse_pos);
+ undo_redo->add_do_method(n2d,"edit_set_pivot",offset+n2d->get_global_transform().affine_inverse().basis_xform(motion_ofs));
+ undo_redo->add_undo_method(n2d,"set_global_pos",gpos);
+ undo_redo->add_undo_method(n2d,"edit_set_pivot",offset);
+ for(int i=0;i<n2d->get_child_count();i++) {
+ Node2D *n2dc = n2d->get_child(i)->cast_to<Node2D>();
+ if (!n2dc)
+ continue;
+
+ undo_redo->add_do_method(n2dc,"set_global_pos",n2dc->get_global_pos());
+ undo_redo->add_undo_method(n2dc,"set_global_pos",n2dc->get_global_pos());
+
+ }
+
+ }
+
+ }
+
+ undo_redo->commit_action();
+ }
+
+ }
+ }
}
@@ -75,9 +242,24 @@ Object *CanvasItemEditor::_get_editor_data(Object *p_what) {
return memnew( CanvasItemEditorSelectedItem );
}
-bool CanvasItemEditor::is_snap_active() const {
+inline float _snap_scalar(float p_offset, float p_step, bool p_snap_relative, float p_target, float p_start) {
+ float offset = p_snap_relative ? p_start : p_offset;
+ return p_step != 0 ? Math::stepify(p_target - offset, p_step) + offset : p_target;
+}
+
+Vector2 CanvasItemEditor::snap_point(Vector2 p_target, Vector2 p_start) const {
+ if (snap_grid) {
+ p_target.x = _snap_scalar(snap_offset.x, snap_step.x, snap_relative, p_target.x, p_start.x);
+ p_target.y = _snap_scalar(snap_offset.y, snap_step.y, snap_relative, p_target.y, p_start.y);
+ }
+ if (snap_pixel)
+ p_target = p_target.snapped(Size2(1, 1));
- return edit_menu->get_popup()->is_item_checked(edit_menu->get_popup()->get_item_index(SNAP_USE));
+ return p_target;
+}
+
+float CanvasItemEditor::snap_angle(float p_target, float p_start) const {
+ return snap_rotation ? _snap_scalar(snap_rotation_offset, snap_rotation_step, snap_relative, p_target, p_start) : p_target;
}
Dictionary CanvasItemEditor::get_state() const {
@@ -86,9 +268,15 @@ Dictionary CanvasItemEditor::get_state() const {
state["zoom"]=zoom;
state["ofs"]=Point2(h_scroll->get_val(),v_scroll->get_val());
// state["ofs"]=-transform.get_origin();
- state["use_snap"]=is_snap_active();
- state["snap"]=snap;
- state["pixel_snap"]=pixel_snap;
+ state["snap_offset"]=snap_offset;
+ state["snap_step"]=snap_step;
+ state["snap_rotation_offset"]=snap_rotation_offset;
+ state["snap_rotation_step"]=snap_rotation_step;
+ state["snap_grid"]=snap_grid;
+ state["snap_show_grid"]=snap_show_grid;
+ state["snap_rotation"]=snap_rotation;
+ state["snap_relative"]=snap_relative;
+ state["snap_pixel"]=snap_pixel;
return state;
}
void CanvasItemEditor::set_state(const Dictionary& p_state){
@@ -105,19 +293,50 @@ void CanvasItemEditor::set_state(const Dictionary& p_state){
v_scroll->set_val(ofs.y);
}
- if (state.has("use_snap")) {
+ if (state.has("snap_step")) {
+ snap_step=state["snap_step"];
+ }
+
+ if (state.has("snap_offset")) {
+ snap_offset=state["snap_offset"];
+ }
+
+ if (state.has("snap_rotation_step")) {
+ snap_rotation_step=state["snap_rotation_step"];
+ }
+
+ if (state.has("snap_rotation_offset")) {
+ snap_rotation_offset=state["snap_rotation_offset"];
+ }
+
+ if (state.has("snap_grid")) {
+ snap_grid=state["snap_grid"];
int idx = edit_menu->get_popup()->get_item_index(SNAP_USE);
- edit_menu->get_popup()->set_item_checked(idx,state["use_snap"]);
+ edit_menu->get_popup()->set_item_checked(idx,snap_grid);
+ }
+
+ if (state.has("snap_show_grid")) {
+ snap_show_grid=state["snap_show_grid"];
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_SHOW_GRID);
+ edit_menu->get_popup()->set_item_checked(idx,snap_show_grid);
}
- if (state.has("snap")) {
- snap=state["snap"];
+ if (state.has("snap_rotation")) {
+ snap_rotation=state["snap_rotation"];
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_ROTATION);
+ edit_menu->get_popup()->set_item_checked(idx,snap_rotation);
}
- if (state.has("pixel_snap")) {
- pixel_snap=state["pixel_snap"];
+ if (state.has("snap_relative")) {
+ snap_relative=state["snap_relative"];
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_RELATIVE);
+ edit_menu->get_popup()->set_item_checked(idx,snap_relative);
+ }
+
+ if (state.has("snap_pixel")) {
+ snap_pixel=state["snap_pixel"];
int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_PIXEL);
- edit_menu->get_popup()->set_item_checked(idx,pixel_snap);
+ edit_menu->get_popup()->set_item_checked(idx,snap_pixel);
}
}
@@ -228,6 +447,47 @@ CanvasItem* CanvasItemEditor::_select_canvas_item_at_pos(const Point2& p_pos,Nod
return NULL;
}
+void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform, Vector<_SelectResult> &r_items) {
+ if (!p_node)
+ return;
+ if (p_node->cast_to<Viewport>())
+ return;
+
+ CanvasItem *c=p_node->cast_to<CanvasItem>();
+
+ for (int i=p_node->get_child_count()-1;i>=0;i--) {
+
+ if (c && !c->is_set_as_toplevel())
+ _find_canvas_items_at_pos(p_pos,p_node->get_child(i),p_parent_xform * c->get_transform(),p_canvas_xform, r_items);
+ else {
+ CanvasLayer *cl = p_node->cast_to<CanvasLayer>();
+ if (cl)
+ return;
+ _find_canvas_items_at_pos(p_pos,p_node->get_child(i),transform ,cl ? cl->get_transform() : p_canvas_xform, r_items); //use base transform
+ }
+ }
+
+
+ if (c && c->is_visible() && !c->has_meta("_edit_lock_")) {
+
+ Rect2 rect = c->get_item_rect();
+ Point2 local_pos = (p_parent_xform * p_canvas_xform * c->get_transform()).affine_inverse().xform(p_pos);
+
+
+ if (rect.has_point(local_pos)) {
+ Node2D *node=c->cast_to<Node2D>();
+
+ _SelectResult res;
+ res.item=c;
+ res.z=node?node->get_z():0;
+ res.has_z=node;
+ r_items.push_back(res);
+ }
+
+ }
+
+ return;
+}
void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2& p_rect,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform,List<CanvasItem*> *r_items) {
@@ -270,6 +530,96 @@ void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2& p_rect,Node* p_no
}
+bool CanvasItemEditor::_select(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag) {
+
+ if (p_append) {
+ //additive selection
+
+ if (!item) {
+
+ if (p_drag) {
+ drag_from=transform.affine_inverse().xform(p_click_pos);
+
+ box_selecting=true;
+ box_selecting_to=drag_from;
+ }
+
+ return false; //nothing to add
+ }
+
+ if (editor_selection->is_selected(item)) {
+ //already in here, erase it
+ editor_selection->remove_node(item);
+ //_remove_canvas_item(c);
+
+ viewport->update();
+ return false;
+
+ }
+ _append_canvas_item(item);
+ viewport->update();
+
+ } else {
+ //regular selection
+
+ if (!item) {
+ //clear because nothing clicked
+ editor_selection->clear();;
+
+ if (p_drag) {
+ drag_from=transform.affine_inverse().xform(p_click_pos);
+
+ box_selecting=true;
+ box_selecting_to=drag_from;
+ }
+
+ viewport->update();
+ return false;
+ }
+
+ if (!editor_selection->is_selected(item)) {
+ //select a new one and clear previous selection
+ editor_selection->clear();
+ editor_selection->add_node(item);
+ //reselect
+ if (get_tree()->is_editor_hint()) {
+ editor->call("edit_node",item);
+ }
+
+ }
+
+ if (p_drag) {
+ //prepare to move!
+
+ List<Node*> &selection = editor_selection->get_selected_node_list();
+
+ for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
+
+ CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
+ if (!canvas_item || !canvas_item->is_visible())
+ continue;
+ CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
+ if (!se)
+ continue;
+
+ se->undo_state=canvas_item->edit_get_state();
+ if (canvas_item->cast_to<Node2D>())
+ se->undo_pivot=canvas_item->cast_to<Node2D>()->edit_get_pivot();
+
+ }
+
+ drag=DRAG_ALL;
+ drag_from=transform.affine_inverse().xform(p_click_pos);
+ drag_point_from=_find_topleftmost_point();
+ }
+
+ viewport->update();
+
+ return true;
+
+ }
+}
+
void CanvasItemEditor::_key_move(const Vector2& p_dir, bool p_snap, KeyMoveMODE p_move_mode) {
@@ -286,9 +636,7 @@ void CanvasItemEditor::_key_move(const Vector2& p_dir, bool p_snap, KeyMoveMODE
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (!se)
@@ -300,7 +648,7 @@ void CanvasItemEditor::_key_move(const Vector2& p_dir, bool p_snap, KeyMoveMODE
Vector2 drag = p_dir;
if (p_snap)
- drag*=snap;
+ drag*=snap_step;
undo_redo->add_undo_method(canvas_item,"edit_set_state",canvas_item->edit_get_state());
@@ -347,9 +695,7 @@ Point2 CanvasItemEditor::_find_topleftmost_point() {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
@@ -377,9 +723,7 @@ int CanvasItemEditor::get_item_count() {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
ic++;
@@ -398,9 +742,7 @@ CanvasItem *CanvasItemEditor::get_single_item() {
for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
if (single_item)
@@ -554,6 +896,11 @@ void CanvasItemEditor::_append_canvas_item(CanvasItem *c) {
}
+void CanvasItemEditor::_snap_changed() {
+ ((SnapDialog *)snap_dialog)->get_fields(snap_offset, snap_step, snap_rotation_offset, snap_rotation_step);
+ viewport->update();
+}
+
void CanvasItemEditor::_dialog_value_changed(double) {
if (updating_value_dialog)
@@ -561,11 +908,6 @@ void CanvasItemEditor::_dialog_value_changed(double) {
switch(last_option) {
- case SNAP_CONFIGURE: {
-
- snap=dialog_val->get_val();
- viewport->update();
- } break;
case ZOOM_SET: {
zoom=dialog_val->get_val()/100.0;
@@ -577,6 +919,24 @@ void CanvasItemEditor::_dialog_value_changed(double) {
}
}
+void CanvasItemEditor::_selection_result_pressed(int p_result) {
+
+ if (selection_results.size() <= p_result)
+ return;
+
+ CanvasItem *item=selection_results[p_result].item;
+
+ if (item)
+ _select(item, Point2(), additive_selection, false);
+}
+
+void CanvasItemEditor::_selection_menu_hide() {
+
+ selection_results.clear();
+ selection_menu->clear();
+ selection_menu->set_size(Vector2(0, 0));
+}
+
bool CanvasItemEditor::get_remove_list(List<Node*> *p_list) {
@@ -639,7 +999,60 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
if (b.button_index==BUTTON_RIGHT) {
+ if (b.pressed && tool==TOOL_SELECT && b.mod.alt) {
+
+ Point2 click=Point2(b.x,b.y);
+
+ Node* scene = editor->get_edited_scene();
+ if (!scene)
+ return;
+
+ _find_canvas_items_at_pos(click, scene,transform,Matrix32(), selection_results);
+
+ if (selection_results.size() == 1) {
+
+ CanvasItem *item = selection_results[0].item;
+ selection_results.clear();
+
+ additive_selection=b.mod.shift;
+ if (!_select(item, click, additive_selection, false))
+ return;
+
+ } else if (!selection_results.empty()) {
+ selection_results.sort();
+
+ NodePath root_path = get_tree()->get_edited_scene_root()->get_path();
+ StringName root_name = root_path.get_name(root_path.get_name_count()-1);
+
+ for (int i = 0; i < selection_results.size(); i++) {
+
+ CanvasItem *item=selection_results[i].item;
+
+ Ref<Texture> icon;
+ if (item->has_meta("_editor_icon"))
+ icon=item->get_meta("_editor_icon");
+ else
+ icon=get_icon( has_icon(item->get_type(),"EditorIcons")?item->get_type():String("Object"),"EditorIcons");
+
+ String node_path="/"+root_name+"/"+root_path.rel_path_to(item->get_path());
+
+ selection_menu->add_item(item->get_name());
+ selection_menu->set_item_icon(i, icon );
+ selection_menu->set_item_metadata(i, node_path);
+ selection_menu->set_item_tooltip(i,String(item->get_name())+
+ "\nType: "+item->get_type()+"\nPath: "+node_path);
+ }
+
+ additive_selection=b.mod.shift;
+
+ selection_menu->set_global_pos(Vector2( b.global_x, b.global_y ));
+ selection_menu->popup();
+ selection_menu->call_deferred("grab_click_focus");
+
+ return;
+ }
+ }
if (get_item_count() > 0 && drag!=DRAG_NONE) {
//cancel drag
@@ -661,9 +1074,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
@@ -735,9 +1146,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (!se)
@@ -799,13 +1208,13 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
}
- List<BoneList>::Element *Cbone=NULL; //closest
+ Map<ObjectID,BoneList>::Element *Cbone=NULL; //closest
{
bone_ik_list.clear();
float closest_dist=1e20;
int bone_width = EditorSettings::get_singleton()->get("2d_editor/bone_width");
- for(List<BoneList>::Element *E=bone_list.front();E;E=E->next()) {
+ for(Map<ObjectID,BoneList>::Element *E=bone_list.front();E;E=E->next()) {
if (E->get().from == E->get().to)
continue;
@@ -943,9 +1352,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (!se)
@@ -1010,90 +1417,16 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
#if 0
if ( b.pressed ) box_selection_start( click );
#endif
- if (b.mod.shift) { //additive selection
-
- if (!c) {
-
- drag_from=transform.affine_inverse().xform(click);
-
- box_selecting=true;
- box_selecting_to=drag_from;
- return; //nothing to add
- }
-
- if (editor_selection->is_selected(c)) {
- //already in here, erase it
- editor_selection->remove_node(c);
- //_remove_canvas_item(c);
-
- viewport->update();
- return;
-
- }
- _append_canvas_item(c);
- viewport->update();
- } else {
- //regular selection
-
-
-
- if (!c) {
- //clear because nothing clicked
- editor_selection->clear();;
-
- drag_from=transform.affine_inverse().xform(click);
-
- box_selecting=true;
- box_selecting_to=drag_from;
- viewport->update();
- return;
- }
-
- if (!editor_selection->is_selected(c)) {
- //select a new one and clear previous selection
- editor_selection->clear();
- editor_selection->add_node(c);
- //reselect
- if (get_tree()->is_editor_hint()) {
- editor->call("edit_node",c);
- }
-
- }
-
- //prepare to move!
-
- List<Node*> &selection = editor_selection->get_selected_node_list();
-
- for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
-
- CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
- continue;
- CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- if (!se)
- continue;
-
- se->undo_state=canvas_item->edit_get_state();
- if (canvas_item->cast_to<Node2D>())
- se->undo_pivot=canvas_item->cast_to<Node2D>()->edit_get_pivot();
-
- }
-
- drag=DRAG_ALL;
- drag_from=transform.affine_inverse().xform(click);
- drag_point_from=_find_topleftmost_point();
- viewport->update();
-
- }
+ additive_selection=b.mod.shift;
+ if (!_select(c, click, additive_selection))
+ return;
}
if (p_event.type==InputEvent::MOUSE_MOTION) {
- if (!viewport->has_focus())
+ if (!viewport->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field()))
viewport->call_deferred("grab_focus");
const InputEventMouseMotion &m=p_event.mouse_motion;
@@ -1126,9 +1459,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (!se)
@@ -1153,39 +1484,21 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
if (drag==DRAG_ROTATE) {
Vector2 center = canvas_item->get_global_transform_with_canvas().get_origin();
-
- Matrix32 rot;
- rot.elements[1] = (dfrom - center).normalized();
- rot.elements[0] = rot.elements[1].tangent();
- float ang = rot.xform_inv(dto-center).atan2();
- canvas_item->edit_rotate(ang);
- display_rotate_to = dto;
- display_rotate_from = center;
+ if (Node2D *node = canvas_item->cast_to<Node2D>()) {
+ Matrix32 rot;
+ rot.elements[1] = (dfrom - center).normalized();
+ rot.elements[0] = rot.elements[1].tangent();
+ node->set_rot(snap_angle(rot.xform_inv(dto-center).angle(), node->get_rot()));
+ display_rotate_to = dto;
+ display_rotate_from = center;
+ viewport->update();
+ }
continue;
}
- if (pixel_snap || (is_snap_active() && snap>0)) {
-
- if (drag!=DRAG_ALL) {
- dfrom=drag_point_from;
- dto=snapify(dto);
- } else {
-
- Vector2 newpos = drag_point_from + (dto-dfrom);
- Vector2 disp;
- if (!is_snap_active() || snap<1) {
-
- disp.x = Math::fposmod(newpos.x,1);
- disp.y = Math::fposmod(newpos.y,1);
-
- } else {
- disp.x = Math::fposmod(newpos.x,snap);
- disp.y = Math::fposmod(newpos.y,snap);
- }
- dto-=disp;
- }
- }
+ dfrom = drag_point_from;
+ dto = snap_point(dto - (drag == DRAG_ALL ? drag_from - drag_point_from : Vector2(0, 0)), drag_point_from);
Vector2 drag_vector =
canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dto) -
@@ -1293,8 +1606,6 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
-
-
if (!dragging_bone) {
local_rect.pos=begin;
@@ -1477,32 +1788,32 @@ void CanvasItemEditor::_viewport_draw() {
_update_scrollbars();
RID ci=viewport->get_canvas_item();
- if (snap>0 && is_snap_active() && true ) {
-
+ if (snap_show_grid) {
Size2 s = viewport->get_size();
-
int last_cell;
Matrix32 xform = transform.affine_inverse();
- for(int i=0;i<s.width;i++) {
- int cell = Math::fast_ftoi(Math::floor(xform.xform(Vector2(i,0)).x/snap));
- if (i==0)
+ if (snap_step.x!=0) {
+ for(int i=0;i<s.width;i++) {
+ int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(i,0)).x-snap_offset.x)/snap_step.x));
+ if (i==0)
+ last_cell=cell;
+ if (last_cell!=cell)
+ viewport->draw_line(Point2(i,0),Point2(i,s.height),Color(0.3,0.7,1,0.3));
last_cell=cell;
- if (last_cell!=cell)
- viewport->draw_line(Point2(i,0),Point2(i,s.height),Color(0.3,0.7,1,0.3));
- last_cell=cell;
+ }
}
- for(int i=0;i<s.height;i++) {
-
- int cell = Math::fast_ftoi(Math::floor(xform.xform(Vector2(0,i)).y/snap));
- if (i==0)
+ if (snap_step.y!=0) {
+ for(int i=0;i<s.height;i++) {
+ int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(0,i)).y-snap_offset.y)/snap_step.y));
+ if (i==0)
+ last_cell=cell;
+ if (last_cell!=cell)
+ viewport->draw_line(Point2(0,i),Point2(s.width,i),Color(0.3,0.7,1,0.3));
last_cell=cell;
- if (last_cell!=cell)
- viewport->draw_line(Point2(0,i),Point2(s.width,i),Color(0.3,0.7,1,0.3));
- last_cell=cell;
+ }
}
-
}
if (viewport->has_focus()) {
@@ -1530,9 +1841,7 @@ void CanvasItemEditor::_viewport_draw() {
CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (!se)
@@ -1569,7 +1878,7 @@ void CanvasItemEditor::_viewport_draw() {
viewport->draw_line(endpoints[i],endpoints[(i+1)%4],c,2);
}
- if (single && (tool==TOOL_SELECT || tool == TOOL_MOVE)) { //kind of sucks
+ if (single && (tool==TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_ROTATE)) { //kind of sucks
if (canvas_item->cast_to<Node2D>()) {
@@ -1673,7 +1982,7 @@ void CanvasItemEditor::_viewport_draw() {
Color bone_ik_color = EditorSettings::get_singleton()->get("2d_editor/bone_ik_color");
Color bone_selected_color = EditorSettings::get_singleton()->get("2d_editor/bone_selected_color");
- for(List<BoneList>::Element*E=bone_list.front();E;E=E->next()) {
+ for(Map<ObjectID,BoneList>::Element*E=bone_list.front();E;E=E->next()) {
E->get().from=Vector2();
E->get().to=Vector2();
@@ -1746,14 +2055,20 @@ void CanvasItemEditor::_notification(int p_what) {
List<Node*> &selection = editor_selection->get_selected_node_list();
+ bool all_control=true;
+ bool has_control=false;
+
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
+ if (canvas_item->cast_to<Control>())
+ has_control=true;
+ else
+ all_control=false;
+
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (!se)
continue;
@@ -1770,10 +2085,19 @@ void CanvasItemEditor::_notification(int p_what) {
}
- for(List<BoneList>::Element *E=bone_list.front();E;E=E->next()) {
+ bool show_anchor = all_control && has_control;
+ if (show_anchor != !anchor_menu->is_hidden()) {
+ if (show_anchor)
+ anchor_menu->show();
+ else
+ anchor_menu->hide();
+ }
+
+ for(Map<ObjectID,BoneList>::Element *E=bone_list.front();E;E=E->next()) {
Object *b = ObjectDB::get_instance(E->get().bone);
if (!b) {
+
viewport->update();
break;
}
@@ -1810,6 +2134,32 @@ void CanvasItemEditor::_notification(int p_what) {
ungroup_button->set_icon(get_icon("Ungroup","EditorIcons"));
key_insert_button->set_icon(get_icon("Key","EditorIcons"));
+
+ //anchor_menu->add_icon_override("Align Top Left");
+ anchor_menu->set_icon(get_icon("Anchor","EditorIcons"));
+ PopupMenu *p=anchor_menu->get_popup();
+
+ p->add_icon_item(get_icon("ControlAlignTopLeft","EditorIcons"),"Top Left",ANCHOR_ALIGN_TOP_LEFT);
+ p->add_icon_item(get_icon("ControlAlignTopRight","EditorIcons"),"Top Right",ANCHOR_ALIGN_TOP_RIGHT);
+ p->add_icon_item(get_icon("ControlAlignBottomRight","EditorIcons"),"Bottom Right",ANCHOR_ALIGN_BOTTOM_RIGHT);
+ p->add_icon_item(get_icon("ControlAlignBottomLeft","EditorIcons"),"Bottom Left",ANCHOR_ALIGN_BOTTOM_LEFT);
+ p->add_separator();
+ p->add_icon_item(get_icon("ControlAlignLeftCenter","EditorIcons"),"Center Left",ANCHOR_ALIGN_CENTER_LEFT);
+ p->add_icon_item(get_icon("ControlAlignTopCenter","EditorIcons"),"Center Top",ANCHOR_ALIGN_CENTER_TOP);
+ p->add_icon_item(get_icon("ControlAlignRightCenter","EditorIcons"),"Center Right",ANCHOR_ALIGN_CENTER_RIGHT);
+ p->add_icon_item(get_icon("ControlAlignBottomCenter","EditorIcons"),"Center Bottom",ANCHOR_ALIGN_CENTER_BOTTOM);
+ p->add_icon_item(get_icon("ControlAlignCenter","EditorIcons"),"Center",ANCHOR_ALIGN_CENTER);
+ p->add_separator();
+ p->add_icon_item(get_icon("ControlAlignLeftWide","EditorIcons"),"Left Wide",ANCHOR_ALIGN_LEFT_WIDE);
+ p->add_icon_item(get_icon("ControlAlignTopWide","EditorIcons"),"Top Wide",ANCHOR_ALIGN_TOP_WIDE);
+ p->add_icon_item(get_icon("ControlAlignRightWide","EditorIcons"),"Right Wide",ANCHOR_ALIGN_RIGHT_WIDE);
+ p->add_icon_item(get_icon("ControlAlignBottomWide","EditorIcons"),"Bottom Wide",ANCHOR_ALIGN_BOTTOM_WIDE);
+ p->add_icon_item(get_icon("ControlVcenterWide","EditorIcons"),"VCenter Wide ",ANCHOR_ALIGN_VCENTER_WIDE);
+ p->add_icon_item(get_icon("ControlHcenterWide","EditorIcons"),"HCenter Wide ",ANCHOR_ALIGN_HCENTER_WIDE);
+ p->add_separator();
+ p->add_icon_item(get_icon("ControlAlignWide","EditorIcons"),"Full Rect",ANCHOR_ALIGN_WIDE);
+
+
}
if (p_what==NOTIFICATION_READY) {
@@ -1875,9 +2225,14 @@ void CanvasItemEditor::_find_canvas_items_span(Node *p_node, Rect2& r_rect, cons
if (c->has_meta("_edit_bone_")) {
- BoneList bone;
- bone.bone=c->get_instance_ID();
- bone_list.push_back(bone);
+ ObjectID id = c->get_instance_ID();
+ if (!bone_list.has(id)) {
+ BoneList bone;
+ bone.bone=id;
+ bone_list[id]=bone;
+ }
+
+ bone_list[id].last_pass=bone_last_frame;
}
r_rect.expand_to( xform.xform(rect.pos) );
@@ -1912,11 +2267,26 @@ void CanvasItemEditor::_update_scrollbars() {
Rect2 canvas_item_rect=Rect2(Point2(),screen_rect);
lock_list.clear();;
- bone_list.clear();;
+ bone_last_frame++;
+
+
if (editor->get_edited_scene())
_find_canvas_items_span(editor->get_edited_scene(),canvas_item_rect,Matrix32());
+ List<Map<ObjectID,BoneList>::Element*> bone_to_erase;
+
+ for(Map<ObjectID,BoneList>::Element*E=bone_list.front();E;E=E->next()) {
+
+ if (E->get().last_pass!=bone_last_frame) {
+ bone_to_erase.push_back(E);
+ }
+ }
+
+ while(bone_to_erase.size()) {
+ bone_list.erase(bone_to_erase.front()->get());
+ bone_to_erase.pop_front();
+ }
//expand area so it's easier to do animations and stuff at 0,0
canvas_item_rect.size+=screen_rect*2;
@@ -1995,60 +2365,62 @@ void CanvasItemEditor::_update_scroll(float) {
}
+void CanvasItemEditor::_set_anchor(Control::AnchorType p_left,Control::AnchorType p_top,Control::AnchorType p_right,Control::AnchorType p_bottom) {
+ List<Node*> &selection = editor_selection->get_selected_node_list();
-Point2 CanvasItemEditor::snapify(const Point2& p_pos) const {
-
- bool active=is_snap_active();
-
- Vector2 pos = p_pos;
-
- if (!active || snap<1) {
-
- if (pixel_snap) {
+ undo_redo->create_action("Change Anchors");
+ for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
- pos.x=Math::stepify(pos.x,1);
- pos.y=Math::stepify(pos.y,1);
- }
+ Control *c = E->get()->cast_to<Control>();
- return pos;
+ undo_redo->add_do_method(c,"set_anchor",MARGIN_LEFT,p_left);
+ undo_redo->add_do_method(c,"set_anchor",MARGIN_TOP,p_top);
+ undo_redo->add_do_method(c,"set_anchor",MARGIN_RIGHT,p_right);
+ undo_redo->add_do_method(c,"set_anchor",MARGIN_BOTTOM,p_bottom);
+ undo_redo->add_undo_method(c,"set_anchor",MARGIN_LEFT,c->get_anchor(MARGIN_LEFT));
+ undo_redo->add_undo_method(c,"set_anchor",MARGIN_TOP,c->get_anchor(MARGIN_TOP));
+ undo_redo->add_undo_method(c,"set_anchor",MARGIN_RIGHT,c->get_anchor(MARGIN_RIGHT));
+ undo_redo->add_undo_method(c,"set_anchor",MARGIN_BOTTOM,c->get_anchor(MARGIN_BOTTOM));
}
-
- pos.x=Math::stepify(pos.x,snap);
- pos.y=Math::stepify(pos.y,snap);
- return pos;
-
+ undo_redo->commit_action();
}
-
void CanvasItemEditor::_popup_callback(int p_op) {
last_option=MenuOption(p_op);
switch(p_op) {
case SNAP_USE: {
-
+ snap_grid = !snap_grid;
int idx = edit_menu->get_popup()->get_item_index(SNAP_USE);
- edit_menu->get_popup()->set_item_checked( idx,!edit_menu->get_popup()->is_item_checked(0));
+ edit_menu->get_popup()->set_item_checked(idx,snap_grid);
+ } break;
+ case SNAP_SHOW_GRID: {
+ snap_show_grid = !snap_show_grid;
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_SHOW_GRID);
+ edit_menu->get_popup()->set_item_checked(idx,snap_show_grid);
viewport->update();
} break;
+ case SNAP_USE_ROTATION: {
+ snap_rotation = !snap_rotation;
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_ROTATION);
+ edit_menu->get_popup()->set_item_checked(idx,snap_rotation);
+ } break;
+ case SNAP_RELATIVE: {
+ snap_relative = !snap_relative;
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_RELATIVE);
+ edit_menu->get_popup()->set_item_checked(idx,snap_relative);
+ } break;
case SNAP_USE_PIXEL: {
- pixel_snap = ! pixel_snap;
+ snap_pixel = !snap_pixel;
int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_PIXEL);
- edit_menu->get_popup()->set_item_checked(idx,pixel_snap);
+ edit_menu->get_popup()->set_item_checked(idx,snap_pixel);
} break;
case SNAP_CONFIGURE: {
- updating_value_dialog=true;
-
- dialog_label->set_text("Snap (Pixels):");
- dialog_val->set_min(1);
- dialog_val->set_step(1);
- dialog_val->set_max(4096);
- dialog_val->set_val(snap);
- value_dialog->popup_centered(Size2(200,85));
- updating_value_dialog=false;
-
+ ((SnapDialog *)snap_dialog)->set_fields(snap_offset, snap_step, snap_rotation_offset, snap_rotation_step);
+ snap_dialog->popup_centered(Size2(220,160));
} break;
case ZOOM_IN: {
zoom=zoom*(1.0/0.5);
@@ -2092,9 +2464,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
canvas_item->set_meta("_edit_lock_",true);
@@ -2109,9 +2479,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
@@ -2129,9 +2497,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
canvas_item->set_meta("_edit_group_",true);
@@ -2146,9 +2512,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
canvas_item->set_meta("_edit_group_",Variant());
@@ -2166,9 +2530,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
@@ -2226,6 +2588,56 @@ void CanvasItemEditor::_popup_callback(int p_op) {
case SPACE_VERTICAL: {
//space_selected_items< proj_vector2_y, compare_items_y >();
} break;
+ case ANCHOR_ALIGN_TOP_LEFT: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_BEGIN);
+ } break;
+ case ANCHOR_ALIGN_TOP_RIGHT: {
+ _set_anchor(ANCHOR_END,ANCHOR_BEGIN,ANCHOR_END,ANCHOR_BEGIN);
+ } break;
+ case ANCHOR_ALIGN_BOTTOM_LEFT: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_END,ANCHOR_BEGIN,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_BOTTOM_RIGHT: {
+ _set_anchor(ANCHOR_END,ANCHOR_END,ANCHOR_END,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_CENTER_LEFT: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_CENTER,ANCHOR_BEGIN,ANCHOR_CENTER);
+ } break;
+ case ANCHOR_ALIGN_CENTER_RIGHT: {
+
+ _set_anchor(ANCHOR_END,ANCHOR_CENTER,ANCHOR_END,ANCHOR_CENTER);
+ } break;
+ case ANCHOR_ALIGN_CENTER_TOP: {
+ _set_anchor(ANCHOR_CENTER,ANCHOR_BEGIN,ANCHOR_CENTER,ANCHOR_BEGIN);
+ } break;
+ case ANCHOR_ALIGN_CENTER_BOTTOM: {
+ _set_anchor(ANCHOR_CENTER,ANCHOR_END,ANCHOR_CENTER,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_CENTER: {
+ _set_anchor(ANCHOR_CENTER,ANCHOR_CENTER,ANCHOR_CENTER,ANCHOR_CENTER);
+ } break;
+ case ANCHOR_ALIGN_TOP_WIDE: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_END,ANCHOR_BEGIN);
+ } break;
+ case ANCHOR_ALIGN_LEFT_WIDE: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_RIGHT_WIDE: {
+ _set_anchor(ANCHOR_END,ANCHOR_BEGIN,ANCHOR_END,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_BOTTOM_WIDE: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_END,ANCHOR_END,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_VCENTER_WIDE: {
+ _set_anchor(ANCHOR_CENTER,ANCHOR_BEGIN,ANCHOR_CENTER,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_HCENTER_WIDE: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_CENTER,ANCHOR_END,ANCHOR_CENTER);
+ } break;
+ case ANCHOR_ALIGN_WIDE: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_END,ANCHOR_END);
+ } break;
+
case ANIM_INSERT_KEY:
case ANIM_INSERT_KEY_EXISTING: {
@@ -2236,9 +2648,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
if (canvas_item->cast_to<Node2D>()) {
@@ -2307,7 +2717,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
} break;
case ANIM_INSERT_ROT: {
- key_pos = key_rot_button->is_pressed();
+ key_rot = key_rot_button->is_pressed();
} break;
case ANIM_INSERT_SCALE: {
@@ -2348,9 +2758,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
@@ -2400,9 +2808,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
if (canvas_item->cast_to<Node2D>()) {
@@ -2528,9 +2934,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
- if (!canvas_item)
- continue;
- if (!canvas_item->is_visible())
+ if (!canvas_item || !canvas_item->is_visible())
continue;
@@ -2604,6 +3008,9 @@ void CanvasItemEditor::_bind_methods() {
ObjectTypeDB::bind_method("_unhandled_key_input",&CanvasItemEditor::_unhandled_key_input);
ObjectTypeDB::bind_method("_viewport_draw",&CanvasItemEditor::_viewport_draw);
ObjectTypeDB::bind_method("_viewport_input_event",&CanvasItemEditor::_viewport_input_event);
+ ObjectTypeDB::bind_method("_snap_changed",&CanvasItemEditor::_snap_changed);
+ ObjectTypeDB::bind_method(_MD("_selection_result_pressed"),&CanvasItemEditor::_selection_result_pressed);
+ ObjectTypeDB::bind_method(_MD("_selection_menu_hide"),&CanvasItemEditor::_selection_menu_hide);
ADD_SIGNAL( MethodInfo("item_lock_status_changed") );
ADD_SIGNAL( MethodInfo("item_group_status_changed") );
@@ -2683,6 +3090,16 @@ void CanvasItemEditor::add_control_to_menu_panel(Control *p_control) {
hb->add_child(p_control);
}
+HSplitContainer *CanvasItemEditor::get_palette_split() {
+
+ return palette_split;
+}
+
+VSplitContainer *CanvasItemEditor::get_bottom_split() {
+
+ return bottom_split;
+}
+
CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
tool = TOOL_SELECT;
@@ -2697,15 +3114,24 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
add_child( hb );
hb->set_area_as_parent_rect();
+ bottom_split = memnew( VSplitContainer );
+ bottom_split->set_v_size_flags(SIZE_EXPAND_FILL);
+ add_child(bottom_split);
+
+ palette_split = memnew( HSplitContainer);
+ palette_split->set_v_size_flags(SIZE_EXPAND_FILL);
+ bottom_split->add_child(palette_split);
+
Control *vp_base = memnew (Control);
- add_child(vp_base);
vp_base->set_v_size_flags(SIZE_EXPAND_FILL);
+ palette_split->add_child(vp_base);
Control *vp = memnew (Control);
vp_base->add_child(vp);
vp->set_area_as_parent_rect();
vp->add_child(p_editor->get_scene_root());
+
viewport = memnew( Control );
vp_base->add_child(viewport);
viewport->set_area_as_parent_rect();
@@ -2735,7 +3161,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
hb->add_child(select_button);
select_button->connect("pressed",this,"_tool_select",make_binds(TOOL_SELECT));
select_button->set_pressed(true);
- select_button->set_tooltip("Select Mode (Q)\n"+keycode_get_string(KEY_MASK_CMD)+"Drag: Rotate\nAlt+Drag: Move\nPress 'v' to Move Pivot (while moving)");
+ select_button->set_tooltip("Select Mode (Q)\n"+keycode_get_string(KEY_MASK_CMD)+"Drag: Rotate\nAlt+Drag: Move\nPress 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving).");
move_button = memnew( ToolButton );
move_button->set_toggle_mode(true);
@@ -2790,6 +3216,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
PopupMenu *p;
p = edit_menu->get_popup();
p->add_check_item("Use Snap",SNAP_USE);
+ p->add_check_item("Show Grid",SNAP_SHOW_GRID);
+ p->add_check_item("Use Rotation Snap",SNAP_USE_ROTATION);
+ p->add_check_item("Snap Relative",SNAP_RELATIVE);
p->add_item("Configure Snap..",SNAP_CONFIGURE);
p->add_separator();
p->add_check_item("Use Pixel Snap",SNAP_USE_PIXEL);
@@ -2829,6 +3258,14 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
p->add_item("Center Selection", VIEW_CENTER_TO_SELECTION, KEY_F);
p->add_item("Frame Selection", VIEW_FRAME_TO_SELECTION, KEY_MASK_CMD|KEY_F);
+ anchor_menu = memnew( MenuButton );
+ anchor_menu->set_text("Anchor");
+ hb->add_child(anchor_menu);
+ anchor_menu->get_popup()->connect("item_pressed", this,"_popup_callback");
+ anchor_menu->hide();
+
+ //p = anchor_menu->get_popup();
+
animation_hb = memnew( HBoxContainer );
@@ -2878,7 +3315,11 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
p->add_separator();
p->add_item("Copy Pose",ANIM_COPY_POSE);
p->add_item("Paste Pose",ANIM_PASTE_POSE);
- p->add_item("Clear Pose",ANIM_CLEAR_POSE,KEY_MASK_ALT|KEY_K);
+ p->add_item("Clear Pose",ANIM_CLEAR_POSE,KEY_MASK_SHIFT|KEY_K);
+
+ snap_dialog = memnew( SnapDialog );
+ snap_dialog->connect("confirmed",this,"_snap_changed");
+ add_child(snap_dialog);
value_dialog = memnew( AcceptDialog );
value_dialog->set_title("Set a Value");
@@ -2899,12 +3340,25 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
dialog_val->connect("value_changed",this,"_dialog_value_changed");
select_sb = Ref<StyleBoxTexture>( memnew( StyleBoxTexture) );
+ selection_menu = memnew( PopupMenu );
+ add_child(selection_menu);
+ selection_menu->set_custom_minimum_size(Vector2(100, 0));
+ selection_menu->connect("item_pressed", this, "_selection_result_pressed");
+ selection_menu->connect("popup_hide", this, "_selection_menu_hide");
+
key_pos=true;
key_rot=true;
key_scale=false;
zoom=1;
- snap=10;
+ snap_offset=Vector2(0, 0);
+ snap_step=Vector2(10, 10);
+ snap_rotation_offset=0;
+ snap_rotation_step=15 / (180 / Math_PI);
+ snap_grid=false;
+ snap_show_grid=false;
+ snap_rotation=false;
+ snap_pixel=false;
updating_value_dialog=false;
box_selecting=false;
//zoom=0.5;
@@ -2912,8 +3366,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
editor->get_animation_editor()->connect("keying_changed",this,"_keying_changed");
set_process_unhandled_key_input(true);
can_move_pivot=false;
- pixel_snap=false;
drag=DRAG_NONE;
+ bone_last_frame=0;
+ additive_selection=false;
}
CanvasItemEditor *CanvasItemEditor::singleton=NULL;
diff --git a/tools/editor/plugins/canvas_item_editor_plugin.h b/tools/editor/plugins/canvas_item_editor_plugin.h
index 15ac7b1bb3..b96d36f7dc 100644
--- a/tools/editor/plugins/canvas_item_editor_plugin.h
+++ b/tools/editor/plugins/canvas_item_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -75,6 +75,9 @@ class CanvasItemEditor : public VBoxContainer {
enum MenuOption {
SNAP_USE,
+ SNAP_SHOW_GRID,
+ SNAP_USE_ROTATION,
+ SNAP_RELATIVE,
SNAP_CONFIGURE,
SNAP_USE_PIXEL,
ZOOM_IN,
@@ -87,6 +90,23 @@ class CanvasItemEditor : public VBoxContainer {
UNGROUP_SELECTED,
ALIGN_HORIZONTAL,
ALIGN_VERTICAL,
+ ANCHOR_ALIGN_TOP_LEFT,
+ ANCHOR_ALIGN_TOP_RIGHT,
+ ANCHOR_ALIGN_BOTTOM_LEFT,
+ ANCHOR_ALIGN_BOTTOM_RIGHT,
+ ANCHOR_ALIGN_CENTER_LEFT,
+ ANCHOR_ALIGN_CENTER_RIGHT,
+ ANCHOR_ALIGN_CENTER_TOP,
+ ANCHOR_ALIGN_CENTER_BOTTOM,
+ ANCHOR_ALIGN_CENTER,
+ ANCHOR_ALIGN_TOP_WIDE,
+ ANCHOR_ALIGN_LEFT_WIDE,
+ ANCHOR_ALIGN_RIGHT_WIDE,
+ ANCHOR_ALIGN_BOTTOM_WIDE,
+ ANCHOR_ALIGN_VCENTER_WIDE,
+ ANCHOR_ALIGN_HCENTER_WIDE,
+ ANCHOR_ALIGN_WIDE,
+
SPACE_HORIZONTAL,
SPACE_VERTICAL,
EXPAND_TO_PARENT,
@@ -130,6 +150,7 @@ class CanvasItemEditor : public VBoxContainer {
};
EditorSelection *editor_selection;
+ bool additive_selection;
Tool tool;
bool first_update;
@@ -143,8 +164,15 @@ class CanvasItemEditor : public VBoxContainer {
Matrix32 transform;
float zoom;
- int snap;
- bool pixel_snap;
+ Vector2 snap_offset;
+ Vector2 snap_step;
+ float snap_rotation_step;
+ float snap_rotation_offset;
+ bool snap_grid;
+ bool snap_show_grid;
+ bool snap_rotation;
+ bool snap_relative;
+ bool snap_pixel;
bool box_selecting;
Point2 box_selecting_to;
bool key_pos;
@@ -156,6 +184,18 @@ class CanvasItemEditor : public VBoxContainer {
MenuOption last_option;
+ struct _SelectResult {
+
+ CanvasItem* item;
+ float z;
+ bool has_z;
+ _FORCE_INLINE_ bool operator<(const _SelectResult& p_rr) const {
+ return has_z && p_rr.has_z ? p_rr.z < z : p_rr.has_z;
+ }
+ };
+
+ Vector<_SelectResult> selection_results;
+
struct LockList {
Point2 pos;
bool lock;
@@ -171,9 +211,12 @@ class CanvasItemEditor : public VBoxContainer {
Vector2 from;
Vector2 to;
ObjectID bone;
+ uint64_t last_pass;
};
- List<BoneList> bone_list;
+ uint64_t bone_last_frame;
+ Map<ObjectID,BoneList> bone_list;
+
Matrix32 bone_orig_xform;
struct BoneIK {
@@ -212,12 +255,15 @@ class CanvasItemEditor : public VBoxContainer {
MenuButton *view_menu;
HBoxContainer *animation_hb;
MenuButton *animation_menu;
+ MenuButton *anchor_menu;
Button *key_loc_button;
Button *key_rot_button;
Button *key_scale_button;
Button *key_insert_button;
+ PopupMenu *selection_menu;
+
//PopupMenu *popup;
DragType drag;
Point2 drag_from;
@@ -245,8 +291,13 @@ class CanvasItemEditor : public VBoxContainer {
int handle_len;
CanvasItem* _select_canvas_item_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform);
+ void _find_canvas_items_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform, Vector<_SelectResult> &r_items);
void _find_canvas_items_at_rect(const Rect2& p_rect,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform,List<CanvasItem*> *r_items);
+ bool _select(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag=true);
+
+ ConfirmationDialog *snap_dialog;
+
AcceptDialog *value_dialog;
Label *dialog_label;
SpinBox *dialog_val;
@@ -261,7 +312,6 @@ class CanvasItemEditor : public VBoxContainer {
DragType _find_drag_type(const Matrix32& p_xform, const Rect2& p_local_rect, const Point2& p_click, Vector2& r_point);
- Point2 snapify(const Point2& p_pos) const;
void _popup_callback(int p_op);
bool updating_scroll;
void _update_scroll(float);
@@ -271,6 +321,10 @@ class CanvasItemEditor : public VBoxContainer {
void _append_canvas_item(CanvasItem *p_item);
void _dialog_value_changed(double);
+ void _snap_changed();
+ void _selection_result_pressed(int);
+ void _selection_menu_hide();
+
UndoRedo *undo_redo;
Point2 _find_topleftmost_point();
@@ -289,6 +343,12 @@ class CanvasItemEditor : public VBoxContainer {
void _viewport_input_event(const InputEvent& p_event);
void _viewport_draw();
+
+ void _set_anchor(Control::AnchorType p_left,Control::AnchorType p_top,Control::AnchorType p_right,Control::AnchorType p_bottom);
+
+ HSplitContainer *palette_split;
+ VSplitContainer *bottom_split;
+
friend class CanvasItemEditorPlugin;
protected:
@@ -330,8 +390,8 @@ protected:
static CanvasItemEditor *singleton;
public:
- bool is_snap_active() const;
- int get_snap() const { return snap; }
+ Vector2 snap_point(Vector2 p_target, Vector2 p_start = Vector2(0, 0)) const;
+ float snap_angle(float p_target, float p_start = 0) const;
Matrix32 get_canvas_transform() const { return transform; }
@@ -341,6 +401,9 @@ public:
void add_control_to_menu_panel(Control *p_control);
+ HSplitContainer *get_palette_split();
+ VSplitContainer *get_bottom_split();
+
Control *get_viewport_control() { return viewport; }
diff --git a/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp b/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp
index 6bae0d2fd0..8eea723126 100644
--- a/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp
+++ b/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp
@@ -4,6 +4,7 @@
#include "os/file_access.h"
#include "tools/editor/editor_settings.h"
+
void CollisionPolygon2DEditor::_notification(int p_what) {
switch(p_what) {
@@ -34,17 +35,6 @@ void CollisionPolygon2DEditor::_node_removed(Node *p_node) {
}
-Vector2 CollisionPolygon2DEditor::snap_point(const Vector2& p_point) const {
-
- if (canvas_item_editor->is_snap_active()) {
-
- return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap());
-
- } else {
- return p_point;
- }
-}
-
void CollisionPolygon2DEditor::_menu_option(int p_option) {
switch(p_option) {
@@ -98,7 +88,7 @@ bool CollisionPolygon2DEditor::forward_input_event(const InputEvent& p_event) {
Vector2 gpoint = Point2(mb.x,mb.y);
Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
- cpoint=snap_point(cpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
Vector<Vector2> poly = node->get_polygon();
@@ -301,7 +291,7 @@ bool CollisionPolygon2DEditor::forward_input_event(const InputEvent& p_event) {
Vector2 gpoint = Point2(mm.x,mm.y);
Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
- cpoint=snap_point(cpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint);
canvas_item_editor->get_viewport_control()->update();
@@ -368,6 +358,7 @@ void CollisionPolygon2DEditor::edit(Node *p_collision_polygon) {
wip.clear();
wip_active=false;
edited_point=-1;
+ canvas_item_editor->get_viewport_control()->update();
} else {
node=NULL;
@@ -389,6 +380,7 @@ void CollisionPolygon2DEditor::_bind_methods() {
CollisionPolygon2DEditor::CollisionPolygon2DEditor(EditorNode *p_editor) {
+ node=NULL;
canvas_item_editor=NULL;
editor=p_editor;
undo_redo = editor->get_undo_redo();
@@ -398,11 +390,13 @@ CollisionPolygon2DEditor::CollisionPolygon2DEditor(EditorNode *p_editor) {
add_child(button_create);
button_create->connect("pressed",this,"_menu_option",varray(MODE_CREATE));
button_create->set_toggle_mode(true);
+ button_create->set_tooltip("Create a new polygon from scratch");
button_edit = memnew( ToolButton );
add_child(button_edit);
button_edit->connect("pressed",this,"_menu_option",varray(MODE_EDIT));
button_edit->set_toggle_mode(true);
+ button_edit->set_tooltip("Edit existing polygon:\nLMB: Move Point.\nCtrl+LMB: Split Segment.\nRMB: Erase Point.");
//add_constant_override("separation",0);
diff --git a/tools/editor/plugins/collision_polygon_2d_editor_plugin.h b/tools/editor/plugins/collision_polygon_2d_editor_plugin.h
index 052019b6c5..f34405b355 100644
--- a/tools/editor/plugins/collision_polygon_2d_editor_plugin.h
+++ b/tools/editor/plugins/collision_polygon_2d_editor_plugin.h
@@ -53,7 +53,6 @@ protected:
static void _bind_methods();
public:
- Vector2 snap_point(const Vector2& p_point) const;
bool forward_input_event(const InputEvent& p_event);
void edit(Node *p_collision_polygon);
CollisionPolygon2DEditor(EditorNode *p_editor);
diff --git a/tools/editor/plugins/collision_polygon_editor_plugin.cpp b/tools/editor/plugins/collision_polygon_editor_plugin.cpp
index b92acb60f9..60683f4eda 100644
--- a/tools/editor/plugins/collision_polygon_editor_plugin.cpp
+++ b/tools/editor/plugins/collision_polygon_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,6 +31,8 @@
#include "os/file_access.h"
#include "tools/editor/editor_settings.h"
#include "scene/3d/camera.h"
+#include "canvas_item_editor_plugin.h"
+
void CollisionPolygonEditor::_notification(int p_what) {
switch(p_what) {
@@ -68,19 +70,6 @@ void CollisionPolygonEditor::_node_removed(Node *p_node) {
}
-Vector2 CollisionPolygonEditor::snap_point(const Vector2& p_point) const {
-
- return p_point;
- /*
- if (canvas_item_editor->is_snap_active()) {
-
- return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap());
-
- } else {
- return p_point;
- } ??? */
-}
-
void CollisionPolygonEditor::_menu_option(int p_option) {
switch(p_option) {
@@ -124,6 +113,7 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const
return false;
Transform gt = node->get_global_transform();
+ Transform gi = gt.affine_inverse();
float depth = node->get_depth()*0.5;
Vector3 n = gt.basis.get_axis(2).normalized();
Plane p(gt.origin+n*depth,n);
@@ -146,9 +136,11 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const
if (!p.intersects_ray(ray_from,ray_dir,&spoint))
break;
+ spoint = gi.xform(spoint);
+
Vector2 cpoint(spoint.x,spoint.y);
- //cpoint=snap_point(cpoint); snap?
+ cpoint=CanvasItemEditor::get_singleton()->snap_point(cpoint);
Vector<Vector2> poly = node->get_polygon();
@@ -360,9 +352,11 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const
if (!p.intersects_ray(ray_from,ray_dir,&spoint))
break;
+ spoint = gi.xform(spoint);
+
Vector2 cpoint(spoint.x,spoint.y);
- //cpoint=snap_point(cpoint);
+ cpoint=CanvasItemEditor::get_singleton()->snap_point(cpoint);
edited_point_pos = cpoint;
_polygon_draw();
@@ -544,6 +538,7 @@ void CollisionPolygonEditor::_bind_methods() {
CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) {
+ node=NULL;
editor=p_editor;
undo_redo = editor->get_undo_redo();
diff --git a/tools/editor/plugins/collision_polygon_editor_plugin.h b/tools/editor/plugins/collision_polygon_editor_plugin.h
index 54b0706149..20a0b3c3f6 100644
--- a/tools/editor/plugins/collision_polygon_editor_plugin.h
+++ b/tools/editor/plugins/collision_polygon_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -90,7 +90,6 @@ protected:
static void _bind_methods();
public:
- Vector2 snap_point(const Vector2& p_point) const;
virtual bool forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event);
void edit(Node *p_collision_polygon);
CollisionPolygonEditor(EditorNode *p_editor);
diff --git a/tools/editor/plugins/collision_shape_2d_editor_plugin.cpp b/tools/editor/plugins/collision_shape_2d_editor_plugin.cpp
new file mode 100644
index 0000000000..7e5d52d17d
--- /dev/null
+++ b/tools/editor/plugins/collision_shape_2d_editor_plugin.cpp
@@ -0,0 +1,573 @@
+#include "collision_shape_2d_editor_plugin.h"
+
+#include "canvas_item_editor_plugin.h"
+
+#include "scene/resources/segment_shape_2d.h"
+#include "scene/resources/shape_line_2d.h"
+#include "scene/resources/circle_shape_2d.h"
+#include "scene/resources/rectangle_shape_2d.h"
+#include "scene/resources/capsule_shape_2d.h"
+#include "scene/resources/convex_polygon_shape_2d.h"
+#include "scene/resources/concave_polygon_shape_2d.h"
+
+Variant CollisionShape2DEditor::get_handle_value(int idx) const {
+
+ switch ( shape_type ) {
+ case CAPSULE_SHAPE: {
+ Ref<CapsuleShape2D> capsule = node->get_shape();
+
+ if (idx==0) {
+ return capsule->get_radius();
+ } else if (idx==1) {
+ return capsule->get_height();
+ }
+
+ } break;
+
+ case CIRCLE_SHAPE: {
+ Ref<CircleShape2D> circle = node->get_shape();
+
+ if (idx==0) {
+ return circle->get_radius();
+ }
+
+ } break;
+
+ case CONCAVE_POLYGON_SHAPE: {
+
+ } break;
+
+ case CONVEX_POLYGON_SHAPE: {
+
+ } break;
+
+ case LINE_SHAPE: {
+ Ref<LineShape2D> line = node->get_shape();
+
+ if (idx==0) {
+ return line->get_d();
+ } else {
+ return line->get_normal();
+ }
+
+ } break;
+
+ case RAY_SHAPE: {
+ Ref<RayShape2D> ray = node->get_shape();
+
+ if (idx==0) {
+ return ray->get_length();
+ }
+
+ } break;
+
+ case RECTANGLE_SHAPE: {
+ Ref<RectangleShape2D> rect = node->get_shape();
+
+ if (idx<2) {
+ return rect->get_extents().abs();
+ }
+
+ } break;
+
+ case SEGMENT_SHAPE: {
+ Ref<SegmentShape2D> seg = node->get_shape();
+
+ if (idx==0) {
+ return seg->get_a();
+ } else if (idx==1) {
+ return seg->get_b();
+ }
+
+ } break;
+ }
+
+ return Variant();
+}
+
+void CollisionShape2DEditor::set_handle(int idx, Point2& p_point) {
+
+ switch ( shape_type ) {
+ case CAPSULE_SHAPE: {
+ if (idx < 2) {
+ Ref<CapsuleShape2D> capsule = node->get_shape();
+
+ real_t parameter = Math::abs(p_point[idx]);
+
+ if (idx==0) {
+ capsule->set_radius(parameter);
+ } else if (idx==1){
+ capsule->set_height(parameter*2 - capsule->get_radius()*2);
+ }
+
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+ } break;
+
+ case CIRCLE_SHAPE: {
+ Ref<CircleShape2D> circle = node->get_shape();
+ circle->set_radius(p_point.length());
+
+ canvas_item_editor->get_viewport_control()->update();
+
+ } break;
+
+ case CONCAVE_POLYGON_SHAPE: {
+
+ } break;
+
+ case CONVEX_POLYGON_SHAPE: {
+
+ } break;
+
+ case LINE_SHAPE: {
+ if (idx<2) {
+ Ref<LineShape2D> line = node->get_shape();
+
+ if (idx==0){
+ line->set_d(p_point.length());
+ }else{
+ line->set_normal(p_point.normalized());
+ }
+
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+
+ } break;
+
+ case RAY_SHAPE: {
+ Ref<RayShape2D> ray = node->get_shape();
+
+ ray->set_length(Math::abs(p_point.y));
+
+ canvas_item_editor->get_viewport_control()->update();
+
+ } break;
+
+ case RECTANGLE_SHAPE: {
+ if (idx<2) {
+ Ref<RectangleShape2D> rect = node->get_shape();
+
+ Vector2 extents = rect->get_extents();
+ extents[idx] = p_point[idx];
+
+ rect->set_extents(extents.abs());
+
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+ } break;
+
+ case SEGMENT_SHAPE: {
+ if (edit_handle < 2) {
+ Ref<SegmentShape2D> seg = node->get_shape();
+
+ if (idx==0) {
+ seg->set_a(p_point);
+ } else if (idx==1) {
+ seg->set_b(p_point);
+ }
+
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+ } break;
+ }
+}
+
+void CollisionShape2DEditor::commit_handle(int idx, Variant& p_org) {
+
+ Control* c = canvas_item_editor->get_viewport_control();
+ undo_redo->create_action("Set Handle");
+
+ switch ( shape_type ) {
+ case CAPSULE_SHAPE: {
+ Ref<CapsuleShape2D> capsule = node->get_shape();
+
+ if (idx==0) {
+ undo_redo->add_do_method(capsule.ptr(),"set_radius",capsule->get_radius());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(capsule.ptr(),"set_radius",p_org);
+ undo_redo->add_do_method(c,"update");
+ } else if (idx==1) {
+ undo_redo->add_do_method(capsule.ptr(),"set_height",capsule->get_height());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(capsule.ptr(),"set_height",p_org);
+ undo_redo->add_undo_method(c,"update");
+ }
+
+ } break;
+
+ case CIRCLE_SHAPE: {
+ Ref<CircleShape2D> circle = node->get_shape();
+
+ undo_redo->add_do_method(circle.ptr(),"set_radius",circle->get_radius());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(circle.ptr(),"set_radius",p_org);
+ undo_redo->add_undo_method(c,"update");
+
+ } break;
+
+ case CONCAVE_POLYGON_SHAPE: {
+
+ } break;
+
+ case CONVEX_POLYGON_SHAPE: {
+
+ } break;
+
+ case LINE_SHAPE: {
+ Ref<LineShape2D> line = node->get_shape();
+
+ if (idx==0) {
+ undo_redo->add_do_method(line.ptr(),"set_d",line->get_d());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(line.ptr(),"set_d",p_org);
+ undo_redo->add_undo_method(c,"update");
+ } else {
+ undo_redo->add_do_method(line.ptr(),"set_normal",line->get_normal());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(line.ptr(),"set_normal",p_org);
+ undo_redo->add_undo_method(c,"update");
+ }
+
+ } break;
+
+ case RAY_SHAPE: {
+ Ref<RayShape2D> ray = node->get_shape();
+
+ undo_redo->add_do_method(ray.ptr(),"set_length",ray->get_length());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(ray.ptr(),"set_length",p_org);
+ undo_redo->add_undo_method(c,"update");
+
+ } break;
+
+ case RECTANGLE_SHAPE: {
+ Ref<RectangleShape2D> rect = node->get_shape();
+
+ undo_redo->add_do_method(rect.ptr(),"set_extents",rect->get_extents());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(rect.ptr(),"set_extents",p_org);
+ undo_redo->add_undo_method(c,"update");
+
+ } break;
+
+ case SEGMENT_SHAPE: {
+ Ref<SegmentShape2D> seg = node->get_shape();
+ if (idx==0) {
+ undo_redo->add_do_method(seg.ptr(),"set_a",seg->get_a());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(seg.ptr(),"set_a",p_org);
+ undo_redo->add_undo_method(c,"update");
+ } else if (idx==1) {
+ undo_redo->add_do_method(seg.ptr(),"set_b",seg->get_b());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(seg.ptr(),"set_b",p_org);
+ undo_redo->add_undo_method(c,"update");
+ }
+
+ } break;
+ }
+
+ undo_redo->commit_action();
+}
+
+bool CollisionShape2DEditor::forward_input_event(const InputEvent& p_event) {
+
+ if (!node) {
+ return false;
+ }
+
+ if (!node->get_shape().is_valid()) {
+ return false;
+ }
+
+ if (shape_type == -1) {
+ return false;
+ }
+
+ switch( p_event.type ) {
+ case InputEvent::MOUSE_BUTTON: {
+ const InputEventMouseButton& mb = p_event.mouse_button;
+
+ Matrix32 gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+
+ Point2 gpoint(mb.x,mb.y);
+
+ if (mb.button_index == BUTTON_LEFT) {
+ if (mb.pressed) {
+ for (int i = 0; i < handles.size(); i++) {
+ if (gt.xform(handles[i]).distance_to(gpoint) < 8) {
+ edit_handle = i;
+
+ break;
+ }
+ }
+
+ if (edit_handle==-1) {
+ pressed = false;
+
+ return false;
+ }
+
+ original = get_handle_value(edit_handle);
+ pressed = true;
+
+ return true;
+
+ } else {
+ if (pressed) {
+ commit_handle(edit_handle, original);
+
+ edit_handle = -1;
+ pressed = false;
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+
+ } break;
+
+ case InputEvent::MOUSE_MOTION: {
+ const InputEventMouseMotion& mm = p_event.mouse_motion;
+
+ if (edit_handle == -1 || !pressed) {
+ return false;
+ }
+
+ Point2 gpoint = Point2(mm.x,mm.y);
+ Point2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint = canvas_item_editor->snap_point(cpoint);
+ cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
+
+ set_handle(edit_handle, cpoint);
+
+ return true;
+
+ } break;
+ }
+
+ return false;
+}
+
+void CollisionShape2DEditor::_get_current_shape_type() {
+
+ if (!node) {
+ return;
+ }
+
+ Ref<Shape2D> s = node->get_shape();
+
+ if (!s.is_valid()) {
+ return;
+ }
+
+ if (s->cast_to<CapsuleShape2D>()) {
+ shape_type = CAPSULE_SHAPE;
+ } else if (s->cast_to<CircleShape2D>()) {
+ shape_type = CIRCLE_SHAPE;
+ } else if (s->cast_to<ConcavePolygonShape2D>()) {
+ shape_type = CONCAVE_POLYGON_SHAPE;
+ } else if (s->cast_to<ConvexPolygonShape2D>()) {
+ shape_type = CONVEX_POLYGON_SHAPE;
+ } else if (s->cast_to<LineShape2D>()) {
+ shape_type = LINE_SHAPE;
+ } else if (s->cast_to<RayShape2D>()) {
+ shape_type = RAY_SHAPE;
+ } else if (s->cast_to<RectangleShape2D>()) {
+ shape_type = RECTANGLE_SHAPE;
+ } else if (s->cast_to<SegmentShape2D>()) {
+ shape_type = SEGMENT_SHAPE;
+ } else {
+ shape_type = -1;
+ }
+
+ canvas_item_editor->get_viewport_control()->update();
+}
+
+void CollisionShape2DEditor::_canvas_draw() {
+
+ if (!node) {
+ return;
+ }
+
+ if (!node->get_shape().is_valid()) {
+ return;
+ }
+
+ _get_current_shape_type();
+
+ if (shape_type == -1) {
+ return;
+ }
+
+ Control *c = canvas_item_editor->get_viewport_control();
+ Matrix32 gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+
+ Ref<Texture> h = get_icon("EditorHandle","EditorIcons");
+ Vector2 size = h->get_size()*0.5;
+
+ handles.clear();
+
+ switch (shape_type) {
+ case CAPSULE_SHAPE: {
+ Ref<CapsuleShape2D> shape = node->get_shape();
+
+ handles.resize(2);
+ float radius = shape->get_radius();
+ float height = shape->get_height()/2;
+
+ handles[0] = Point2(radius, -height);
+ handles[1] = Point2(0,-(height + radius));
+
+ c->draw_texture(h, gt.xform(handles[0])-size);
+ c->draw_texture(h, gt.xform(handles[1])-size);
+
+ } break;
+
+ case CIRCLE_SHAPE: {
+ Ref<CircleShape2D> shape = node->get_shape();
+
+ handles.resize(1);
+ handles[0] = Point2(shape->get_radius(),0);
+
+ c->draw_texture(h, gt.xform(handles[0])-size);
+
+ } break;
+
+ case CONCAVE_POLYGON_SHAPE: {
+
+ } break;
+
+ case CONVEX_POLYGON_SHAPE: {
+
+ } break;
+
+ case LINE_SHAPE: {
+ Ref<LineShape2D> shape = node->get_shape();
+
+ handles.resize(2);
+ handles[0] = shape->get_normal() * shape->get_d();
+ handles[1] = shape->get_normal() * (shape->get_d() + 30.0);
+
+ c->draw_texture(h,gt.xform(handles[0])-size);
+ c->draw_texture(h,gt.xform(handles[1])-size);
+
+ } break;
+
+ case RAY_SHAPE: {
+ Ref<RayShape2D> shape = node->get_shape();
+
+ handles.resize(1);
+ handles[0] = Point2(0,shape->get_length());
+
+ c->draw_texture(h,gt.xform(handles[0])-size);
+
+ } break;
+
+ case RECTANGLE_SHAPE: {
+ Ref<RectangleShape2D> shape = node->get_shape();
+
+ handles.resize(2);
+ Vector2 ext = shape->get_extents();
+ handles[0] = Point2(ext.x,0);
+ handles[1] = Point2(0,-ext.y);
+
+ c->draw_texture(h,gt.xform(handles[0])-size);
+ c->draw_texture(h,gt.xform(handles[1])-size);
+
+ } break;
+
+ case SEGMENT_SHAPE: {
+ Ref<SegmentShape2D> shape = node->get_shape();
+
+ handles.resize(2);
+ handles[0] = shape->get_a();
+ handles[1] = shape->get_b();
+
+ c->draw_texture(h, gt.xform(handles[0])-size);
+ c->draw_texture(h, gt.xform(handles[1])-size);
+
+ } break;
+ }
+}
+
+void CollisionShape2DEditor::edit(Node* p_node) {
+
+ if (!canvas_item_editor) {
+ canvas_item_editor=CanvasItemEditor::get_singleton();
+ }
+
+ if (p_node) {
+ node=p_node->cast_to<CollisionShape2D>();
+
+ if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw");
+
+ _get_current_shape_type();
+
+ } else {
+ edit_handle = -1;
+ shape_type = -1;
+
+ if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw");
+
+ node=NULL;
+ }
+
+ canvas_item_editor->get_viewport_control()->update();
+}
+
+void CollisionShape2DEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_canvas_draw",&CollisionShape2DEditor::_canvas_draw);
+ ObjectTypeDB::bind_method("_get_current_shape_type",&CollisionShape2DEditor::_get_current_shape_type);
+}
+
+CollisionShape2DEditor::CollisionShape2DEditor(EditorNode* p_editor) {
+
+ node = NULL;
+ canvas_item_editor = NULL;
+ editor = p_editor;
+
+ undo_redo = p_editor->get_undo_redo();
+
+ edit_handle = -1;
+ pressed = false;
+}
+
+void CollisionShape2DEditorPlugin::edit(Object* p_obj) {
+
+ collision_shape_2d_editor->edit(p_obj->cast_to<Node>());
+}
+
+bool CollisionShape2DEditorPlugin::handles(Object* p_obj) const {
+
+ return p_obj->is_type("CollisionShape2D");
+}
+
+void CollisionShape2DEditorPlugin::make_visible(bool visible) {
+
+ if (!visible) {
+ edit(NULL);
+ }
+}
+
+CollisionShape2DEditorPlugin::CollisionShape2DEditorPlugin(EditorNode* p_node) {
+
+ editor=p_node;
+
+ collision_shape_2d_editor = memnew( CollisionShape2DEditor(p_node) );
+ p_node->get_gui_base()->add_child(collision_shape_2d_editor);
+}
+
+CollisionShape2DEditorPlugin::~CollisionShape2DEditorPlugin() {
+
+}
diff --git a/tools/editor/plugins/collision_shape_2d_editor_plugin.h b/tools/editor/plugins/collision_shape_2d_editor_plugin.h
new file mode 100644
index 0000000000..75e9b68ea7
--- /dev/null
+++ b/tools/editor/plugins/collision_shape_2d_editor_plugin.h
@@ -0,0 +1,73 @@
+#ifndef COLLISION_SHAPE_2D_EDITOR_PLUGIN_H
+#define COLLISION_SHAPE_2D_EDITOR_PLUGIN_H
+
+#include "tools/editor/editor_plugin.h"
+#include "tools/editor/editor_node.h"
+
+#include "scene/2d/collision_shape_2d.h"
+
+class CanvasItemEditor;
+
+class CollisionShape2DEditor : public Control {
+ OBJ_TYPE(CollisionShape2DEditor, Control);
+
+ enum ShapeType {
+ CAPSULE_SHAPE,
+ CIRCLE_SHAPE,
+ CONCAVE_POLYGON_SHAPE,
+ CONVEX_POLYGON_SHAPE,
+ LINE_SHAPE,
+ RAY_SHAPE,
+ RECTANGLE_SHAPE,
+ SEGMENT_SHAPE
+ };
+
+ EditorNode* editor;
+ UndoRedo* undo_redo;
+ CanvasItemEditor* canvas_item_editor;
+ CollisionShape2D* node;
+
+ Vector<Point2> handles;
+
+ int shape_type;
+ int edit_handle;
+ bool pressed;
+ Variant original;
+
+ Variant get_handle_value(int idx) const;
+ void set_handle(int idx, Point2& p_point);
+ void commit_handle(int idx, Variant& p_org);
+
+ void _get_current_shape_type();
+ void _canvas_draw();
+
+protected:
+ static void _bind_methods();
+
+public:
+ bool forward_input_event(const InputEvent& p_event);
+ void edit(Node* p_node);
+
+ CollisionShape2DEditor(EditorNode* p_editor);
+};
+
+class CollisionShape2DEditorPlugin : public EditorPlugin {
+ OBJ_TYPE(CollisionShape2DEditorPlugin, EditorPlugin);
+
+ CollisionShape2DEditor* collision_shape_2d_editor;
+ EditorNode* editor;
+
+public:
+ virtual bool forward_input_event(const InputEvent& p_event) { return collision_shape_2d_editor->forward_input_event(p_event); }
+
+ virtual String get_name() const { return "CollisionShape2D"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object* p_obj);
+ virtual bool handles(Object* p_obj) const;
+ virtual void make_visible(bool visible);
+
+ CollisionShape2DEditorPlugin(EditorNode* p_editor);
+ ~CollisionShape2DEditorPlugin();
+};
+
+#endif //COLLISION_SHAPE_2D_EDITOR_PLUGIN_H
diff --git a/tools/editor/plugins/color_ramp_editor_plugin.cpp b/tools/editor/plugins/color_ramp_editor_plugin.cpp
new file mode 100644
index 0000000000..df50535402
--- /dev/null
+++ b/tools/editor/plugins/color_ramp_editor_plugin.cpp
@@ -0,0 +1,83 @@
+/*
+ * color_ramp_editor_plugin.cpp
+ */
+
+#include "color_ramp_editor_plugin.h"
+
+ColorRampEditorPlugin::ColorRampEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ ramp_editor = memnew( ColorRampEdit );
+
+ add_custom_control(CONTAINER_CANVAS_EDITOR_BOTTOM,ramp_editor);
+ //add_custom_control(CONTAINER_SPATIAL_EDITOR_BOTTOM,ramp_editor);
+ ramp_editor->set_custom_minimum_size(Size2(100, 48));
+ ramp_editor->hide();
+ ramp_editor->connect("ramp_changed", this, "ramp_changed");
+}
+
+void ColorRampEditorPlugin::edit(Object *p_object) {
+
+ ColorRamp* color_ramp = p_object->cast_to<ColorRamp>();
+ if (!color_ramp)
+ return;
+ color_ramp_ref = Ref<ColorRamp>(color_ramp);
+ ramp_editor->set_points(color_ramp_ref->get_points());
+}
+
+bool ColorRampEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("ColorRamp");
+}
+
+void ColorRampEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ ramp_editor->show();
+ } else {
+ ramp_editor->hide();
+ }
+
+}
+
+void ColorRampEditorPlugin::_ramp_changed() {
+
+ if(color_ramp_ref.is_valid())
+ {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+
+ //Not sure if I should convert this data to DVector
+ Vector<float> new_offsets=ramp_editor->get_offsets();
+ Vector<Color> new_colors=ramp_editor->get_colors();
+ Vector<float> old_offsets=color_ramp_ref->get_offsets();
+ Vector<Color> old_colors=color_ramp_ref->get_colors();
+
+ if (old_offsets.size()!=new_offsets.size())
+ ur->create_action("Add/Remove Color Ramp Point");
+ else
+ ur->create_action("Modify Color Ramp",true);
+ ur->add_do_method(this,"undo_redo_color_ramp",new_offsets,new_colors);
+ ur->add_undo_method(this,"undo_redo_color_ramp",old_offsets,old_colors);
+ ur->commit_action();
+
+ //color_ramp_ref->set_points(ramp_editor->get_points());
+ }
+}
+
+void ColorRampEditorPlugin::_undo_redo_color_ramp(const Vector<float>& offsets,
+ const Vector<Color>& colors) {
+
+ color_ramp_ref->set_offsets(offsets);
+ color_ramp_ref->set_colors(colors);
+ ramp_editor->set_points(color_ramp_ref->get_points());
+ ramp_editor->update();
+}
+
+ColorRampEditorPlugin::~ColorRampEditorPlugin(){
+}
+
+void ColorRampEditorPlugin::_bind_methods() {
+ ObjectTypeDB::bind_method(_MD("ramp_changed"),&ColorRampEditorPlugin::_ramp_changed);
+ ObjectTypeDB::bind_method(_MD("undo_redo_color_ramp","offsets","colors"),&ColorRampEditorPlugin::_undo_redo_color_ramp);
+}
diff --git a/tools/editor/plugins/color_ramp_editor_plugin.h b/tools/editor/plugins/color_ramp_editor_plugin.h
new file mode 100644
index 0000000000..e39a5d65fe
--- /dev/null
+++ b/tools/editor/plugins/color_ramp_editor_plugin.h
@@ -0,0 +1,37 @@
+/*
+ * color_ramp_editor_plugin.h
+ */
+
+#ifndef TOOLS_EDITOR_PLUGINS_COLOR_RAMP_EDITOR_PLUGIN_H_
+#define TOOLS_EDITOR_PLUGINS_COLOR_RAMP_EDITOR_PLUGIN_H_
+
+#include "tools/editor/editor_plugin.h"
+#include "tools/editor/editor_node.h"
+#include "scene/gui/color_ramp_edit.h"
+
+class ColorRampEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( ColorRampEditorPlugin, EditorPlugin );
+
+ Ref<ColorRamp> color_ramp_ref;
+ ColorRampEdit *ramp_editor;
+ EditorNode *editor;
+
+protected:
+ static void _bind_methods();
+ void _ramp_changed();
+ void _undo_redo_color_ramp(const Vector<float>& offsets, const Vector<Color>& colors);
+
+public:
+ virtual String get_name() const { return "ColorRamp"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_node);
+ virtual bool handles(Object *p_node) const;
+ virtual void make_visible(bool p_visible);
+
+ ColorRampEditorPlugin(EditorNode *p_node);
+ ~ColorRampEditorPlugin();
+
+};
+
+#endif /* TOOLS_EDITOR_PLUGINS_COLOR_RAMP_EDITOR_PLUGIN_H_ */
diff --git a/tools/editor/plugins/control_editor_plugin.cpp b/tools/editor/plugins/control_editor_plugin.cpp
index 8d8e107f41..7348a69665 100644
--- a/tools/editor/plugins/control_editor_plugin.cpp
+++ b/tools/editor/plugins/control_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/plugins/control_editor_plugin.h b/tools/editor/plugins/control_editor_plugin.h
index a229327990..074298d0df 100644
--- a/tools/editor/plugins/control_editor_plugin.h
+++ b/tools/editor/plugins/control_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/plugins/cube_grid_theme_editor_plugin.cpp b/tools/editor/plugins/cube_grid_theme_editor_plugin.cpp
index aad7cf2c6a..c118485083 100644
--- a/tools/editor/plugins/cube_grid_theme_editor_plugin.cpp
+++ b/tools/editor/plugins/cube_grid_theme_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -269,8 +269,8 @@ void MeshLibraryEditor::_bind_methods() {
MeshLibraryEditor::MeshLibraryEditor(EditorNode *p_editor) {
- file = memnew( FileDialog );
- file->set_mode(FileDialog::MODE_OPEN_FILE);
+ file = memnew( EditorFileDialog );
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
//not for now?
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions);
diff --git a/tools/editor/plugins/cube_grid_theme_editor_plugin.h b/tools/editor/plugins/cube_grid_theme_editor_plugin.h
index 0dab1d12b8..583ddf6e14 100644
--- a/tools/editor/plugins/cube_grid_theme_editor_plugin.h
+++ b/tools/editor/plugins/cube_grid_theme_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -42,7 +42,7 @@ class MeshLibraryEditor : public Control {
EditorNode *editor;
MenuButton *menu;
ConfirmationDialog *cd;
- FileDialog *file;
+ EditorFileDialog *file;
int to_erase;
enum {
diff --git a/tools/editor/plugins/editor_preview_plugins.cpp b/tools/editor/plugins/editor_preview_plugins.cpp
new file mode 100644
index 0000000000..5f52d4c3e7
--- /dev/null
+++ b/tools/editor/plugins/editor_preview_plugins.cpp
@@ -0,0 +1,783 @@
+#include "editor_preview_plugins.h"
+#include "io/resource_loader.h"
+#include "tools/editor/editor_settings.h"
+#include "io/file_access_memory.h"
+#include "os/os.h"
+#include "scene/resources/material.h"
+#include "scene/resources/sample.h"
+#include "scene/resources/mesh.h"
+
+bool EditorTexturePreviewPlugin::handles(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"ImageTexture");
+}
+
+Ref<Texture> EditorTexturePreviewPlugin::generate(const RES& p_from) {
+
+ Ref<ImageTexture> tex =p_from;
+ Image img = tex->get_data();
+ if (img.empty())
+ return Ref<Texture>();
+
+ img.clear_mipmaps();
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+ if (img.is_compressed()) {
+ if (img.decompress()!=OK)
+ return Ref<Texture>();
+ } else if (img.get_format()!=Image::FORMAT_RGB && img.get_format()!=Image::FORMAT_RGBA) {
+ img.convert(Image::FORMAT_RGBA);
+ }
+
+ int width,height;
+ if (img.get_width() > thumbnail_size && img.get_width() >= img.get_height()) {
+
+ width=thumbnail_size;
+ height = img.get_height() * thumbnail_size / img.get_width();
+ } else if (img.get_height() > thumbnail_size && img.get_height() >= img.get_width()) {
+
+ height=thumbnail_size;
+ width = img.get_width() * thumbnail_size / img.get_height();
+ } else {
+
+ width=img.get_width();
+ height=img.get_height();
+ }
+
+ img.resize(width,height);
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture ));
+
+ ptex->create_from_image(img,0);
+ return ptex;
+
+}
+
+EditorTexturePreviewPlugin::EditorTexturePreviewPlugin() {
+
+
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+
+Ref<Texture> EditorPackedScenePreviewPlugin::_gen_from_imd(Ref<ResourceImportMetadata> p_imd) {
+
+ if (p_imd.is_null()) {
+ return Ref<Texture>();
+ }
+
+ if (!p_imd->has_option("thumbnail"))
+ return Ref<Texture>();
+
+ Variant tn = p_imd->get_option("thumbnail");
+ //print_line(Variant::get_type_name(tn.get_type()));
+ DVector<uint8_t> thumbnail = tn;
+
+ int len = thumbnail.size();
+ if (len==0)
+ return Ref<Texture>();
+
+
+ DVector<uint8_t>::Read r = thumbnail.read();
+
+ Image img(r.ptr(),len);
+ if (img.empty())
+ return Ref<Texture>();
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture ));
+ ptex->create_from_image(img,0);
+ return ptex;
+
+}
+
+bool EditorPackedScenePreviewPlugin::handles(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"PackedScene");
+}
+Ref<Texture> EditorPackedScenePreviewPlugin::generate(const RES& p_from) {
+
+ Ref<ResourceImportMetadata> imd = p_from->get_import_metadata();
+ return _gen_from_imd(imd);
+}
+
+Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String& p_path) {
+
+ Ref<ResourceImportMetadata> imd = ResourceLoader::load_import_metadata(p_path);
+ return _gen_from_imd(imd);
+}
+
+EditorPackedScenePreviewPlugin::EditorPackedScenePreviewPlugin() {
+
+}
+
+//////////////////////////////////////////////////////////////////
+
+bool EditorMaterialPreviewPlugin::handles(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"Material"); //any material
+}
+
+Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES& p_from) {
+
+ Ref<Material> material = p_from;
+ ERR_FAIL_COND_V(material.is_null(),Ref<Texture>());
+
+ VS::get_singleton()->mesh_surface_set_material(sphere,0,material->get_rid());
+
+ VS::get_singleton()->viewport_queue_screen_capture(viewport);
+ VS::get_singleton()->viewport_set_render_target_update_mode(viewport,VS::RENDER_TARGET_UPDATE_ONCE); //once used for capture
+// print_line("queue capture!");
+ Image img;
+
+ int timeout=1000;
+ while(timeout) {
+ //print_line("try capture?");
+ OS::get_singleton()->delay_usec(10);
+ img = VS::get_singleton()->viewport_get_screen_capture(viewport);
+ if (!img.empty())
+ break;
+ timeout--;
+ }
+
+ //print_line("captured!");
+ VS::get_singleton()->mesh_surface_set_material(sphere,0,RID());
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+ img.resize(thumbnail_size,thumbnail_size);
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture ));
+ ptex->create_from_image(img,0);
+ return ptex;
+}
+
+EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() {
+
+ scenario = VS::get_singleton()->scenario_create();
+
+ viewport = VS::get_singleton()->viewport_create();
+ VS::get_singleton()->viewport_set_as_render_target(viewport,true);
+ VS::get_singleton()->viewport_set_render_target_update_mode(viewport,VS::RENDER_TARGET_UPDATE_DISABLED);
+ VS::get_singleton()->viewport_set_scenario(viewport,scenario);
+ VS::ViewportRect vr;
+ vr.x=0;
+ vr.y=0;
+ vr.width=128;
+ vr.height=128;
+ VS::get_singleton()->viewport_set_rect(viewport,vr);
+
+ camera = VS::get_singleton()->camera_create();
+ VS::get_singleton()->viewport_attach_camera(viewport,camera);
+ VS::get_singleton()->camera_set_transform(camera,Transform(Matrix3(),Vector3(0,0,3)));
+ VS::get_singleton()->camera_set_perspective(camera,45,0.1,10);
+
+ light = VS::get_singleton()->light_create(VS::LIGHT_DIRECTIONAL);
+ light_instance = VS::get_singleton()->instance_create2(light,scenario);
+ VS::get_singleton()->instance_set_transform(light_instance,Transform().looking_at(Vector3(-1,-1,-1),Vector3(0,1,0)));
+
+ light2 = VS::get_singleton()->light_create(VS::LIGHT_DIRECTIONAL);
+ VS::get_singleton()->light_set_color(light2,VS::LIGHT_COLOR_DIFFUSE,Color(0.7,0.7,0.7));
+ VS::get_singleton()->light_set_color(light2,VS::LIGHT_COLOR_SPECULAR,Color(0.0,0.0,0.0));
+ light_instance2 = VS::get_singleton()->instance_create2(light2,scenario);
+
+ VS::get_singleton()->instance_set_transform(light_instance2,Transform().looking_at(Vector3(0,1,0),Vector3(0,0,1)));
+
+ sphere = VS::get_singleton()->mesh_create();
+ sphere_instance = VS::get_singleton()->instance_create2(sphere,scenario);
+
+ int lats=32;
+ int lons=32;
+ float radius=1.0;
+
+ DVector<Vector3> vertices;
+ DVector<Vector3> normals;
+ DVector<Vector2> uvs;
+ DVector<float> tangents;
+ Matrix3 tt = Matrix3(Vector3(0,1,0),Math_PI*0.5);
+
+ for(int i = 1; i <= lats; i++) {
+ double lat0 = Math_PI * (-0.5 + (double) (i - 1) / lats);
+ double z0 = Math::sin(lat0);
+ double zr0 = Math::cos(lat0);
+
+ double lat1 = Math_PI * (-0.5 + (double) i / lats);
+ double z1 = Math::sin(lat1);
+ double zr1 = Math::cos(lat1);
+
+ for(int j = lons; j >= 1; j--) {
+
+ double lng0 = 2 * Math_PI * (double) (j - 1) / lons;
+ double x0 = Math::cos(lng0);
+ double y0 = Math::sin(lng0);
+
+ double lng1 = 2 * Math_PI * (double) (j) / lons;
+ double x1 = Math::cos(lng1);
+ double y1 = Math::sin(lng1);
+
+
+ Vector3 v[4]={
+ Vector3(x1 * zr0, z0, y1 *zr0),
+ Vector3(x1 * zr1, z1, y1 *zr1),
+ Vector3(x0 * zr1, z1, y0 *zr1),
+ Vector3(x0 * zr0, z0, y0 *zr0)
+ };
+
+#define ADD_POINT(m_idx) \
+ normals.push_back(v[m_idx]);\
+ vertices.push_back(v[m_idx]*radius);\
+ { Vector2 uv(Math::atan2(v[m_idx].x,v[m_idx].z),Math::atan2(-v[m_idx].y,v[m_idx].z));\
+ uv/=Math_PI;\
+ uv*=4.0;\
+ uv=uv*0.5+Vector2(0.5,0.5);\
+ uvs.push_back(uv);\
+ }\
+ { Vector3 t = tt.xform(v[m_idx]);\
+ tangents.push_back(t.x);\
+ tangents.push_back(t.y);\
+ tangents.push_back(t.z);\
+ tangents.push_back(1.0);\
+ }
+
+
+
+ ADD_POINT(0);
+ ADD_POINT(1);
+ ADD_POINT(2);
+
+ ADD_POINT(2);
+ ADD_POINT(3);
+ ADD_POINT(0);
+ }
+ }
+
+ Array arr;
+ arr.resize(VS::ARRAY_MAX);
+ arr[VS::ARRAY_VERTEX]=vertices;
+ arr[VS::ARRAY_NORMAL]=normals;
+ arr[VS::ARRAY_TANGENT]=tangents;
+ arr[VS::ARRAY_TEX_UV]=uvs;
+ VS::get_singleton()->mesh_add_surface(sphere,VS::PRIMITIVE_TRIANGLES,arr);
+
+}
+
+EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() {
+
+ VS::get_singleton()->free(sphere);
+ VS::get_singleton()->free(sphere_instance);
+ VS::get_singleton()->free(viewport);
+ VS::get_singleton()->free(light);
+ VS::get_singleton()->free(light_instance);
+ VS::get_singleton()->free(light2);
+ VS::get_singleton()->free(light_instance2);
+ VS::get_singleton()->free(camera);
+ VS::get_singleton()->free(scenario);
+
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+static bool _is_text_char(CharType c) {
+
+ return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
+}
+
+bool EditorScriptPreviewPlugin::handles(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"Script");
+}
+
+Ref<Texture> EditorScriptPreviewPlugin::generate(const RES& p_from) {
+
+
+ Ref<Script> scr = p_from;
+ if (scr.is_null())
+ return Ref<Texture>();
+
+ String code = scr->get_source_code().strip_edges();
+ if (code=="")
+ return Ref<Texture>();
+
+ List<String> kwors;
+ scr->get_language()->get_reserved_words(&kwors);
+
+ Set<String> keywords;
+
+ for(List<String>::Element *E=kwors.front();E;E=E->next()) {
+
+ keywords.insert(E->get());
+
+ }
+
+
+ int line = 0;
+ int col=0;
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+ Image img(thumbnail_size,thumbnail_size,0,Image::FORMAT_RGBA);
+
+
+
+ Color bg_color = EditorSettings::get_singleton()->get("text_editor/background_color");
+ bg_color.a=1.0;
+ Color keyword_color = EditorSettings::get_singleton()->get("text_editor/keyword_color");
+ Color text_color = EditorSettings::get_singleton()->get("text_editor/text_color");
+ Color symbol_color = EditorSettings::get_singleton()->get("text_editor/symbol_color");
+ Color comment_color = EditorSettings::get_singleton()->get("text_editor/comment_color");
+
+
+ for(int i=0;i<thumbnail_size;i++) {
+ for(int j=0;j<thumbnail_size;j++) {
+ img.put_pixel(i,j,bg_color);
+ }
+
+ }
+
+ bool prev_is_text=false;
+ bool in_keyword=false;
+ for(int i=0;i<code.length();i++) {
+
+ CharType c = code[i];
+ if (c>32) {
+ if (col<thumbnail_size) {
+ Color color = text_color;
+
+ if (c!='_' && ((c>='!' && c<='/') || (c>=':' && c<='@') || (c>='[' && c<='`') || (c>='{' && c<='~') || c=='\t')) {
+ //make symbol a little visible
+ color=symbol_color;
+ in_keyword=false;
+ } else if (!prev_is_text && _is_text_char(c)) {
+ int pos = i;
+
+ while(_is_text_char(code[pos])) {
+ pos++;
+ }
+ ///print_line("from "+itos(i)+" to "+itos(pos));
+ String word = code.substr(i,pos-i);
+ //print_line("found word: "+word);
+ if (keywords.has(word))
+ in_keyword=true;
+
+ } else if (!_is_text_char(c)) {
+ in_keyword=false;
+ }
+
+ if (in_keyword)
+ color=keyword_color;
+
+ Color ul=color;
+ ul.a*=0.5;
+ img.put_pixel(col,line*2,bg_color.blend(ul));
+ img.put_pixel(col,line*2+1,color);
+
+ prev_is_text=_is_text_char(c);
+ }
+ } else {
+
+ prev_is_text=false;
+ in_keyword=false;
+
+ if (c=='\n') {
+ col=0;
+ line++;
+ if (line>=thumbnail_size/2)
+ break;
+ } else if (c=='\t') {
+ col+=3;
+ }
+ }
+ col++;
+ }
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture));
+
+ ptex->create_from_image(img,0);
+ return ptex;
+
+}
+
+EditorScriptPreviewPlugin::EditorScriptPreviewPlugin() {
+
+
+}
+///////////////////////////////////////////////////////////////////
+
+bool EditorSamplePreviewPlugin::handles(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"Sample");
+}
+
+Ref<Texture> EditorSamplePreviewPlugin::generate(const RES& p_from) {
+
+ Ref<Sample> smp =p_from;
+ ERR_FAIL_COND_V(smp.is_null(),Ref<Texture>());
+
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+
+ DVector<uint8_t> img;
+ int w = thumbnail_size;
+ int h = thumbnail_size;
+ img.resize(w*h*3);
+
+ DVector<uint8_t>::Write imgdata = img.write();
+ uint8_t * imgw = imgdata.ptr();
+ DVector<uint8_t> data = smp->get_data();
+ DVector<uint8_t>::Read sampledata = data.read();
+ const uint8_t *sdata=sampledata.ptr();
+
+ bool stereo = smp->is_stereo();
+ bool _16=smp->get_format()==Sample::FORMAT_PCM16;
+ int len = smp->get_length();
+
+ if (len<1)
+ return Ref<Texture>();
+
+ if (smp->get_format()==Sample::FORMAT_IMA_ADPCM) {
+
+ struct IMA_ADPCM_State {
+
+ int16_t step_index;
+ int32_t predictor;
+ /* values at loop point */
+ int16_t loop_step_index;
+ int32_t loop_predictor;
+ int32_t last_nibble;
+ int32_t loop_pos;
+ int32_t window_ofs;
+ const uint8_t *ptr;
+ } ima_adpcm;
+
+ ima_adpcm.step_index=0;
+ ima_adpcm.predictor=0;
+ ima_adpcm.loop_step_index=0;
+ ima_adpcm.loop_predictor=0;
+ ima_adpcm.last_nibble=-1;
+ ima_adpcm.loop_pos=0x7FFFFFFF;
+ ima_adpcm.window_ofs=0;
+ ima_adpcm.ptr=NULL;
+
+
+ for(int i=0;i<w;i++) {
+
+ float max[2]={-1e10,-1e10};
+ float min[2]={1e10,1e10};
+ int from = i*len/w;
+ int to = (i+1)*len/w;
+ if (to>=len)
+ to=len-1;
+
+ for(int j=from;j<to;j++) {
+
+ while(j>ima_adpcm.last_nibble) {
+
+ static const int16_t _ima_adpcm_step_table[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+ };
+
+ static const int8_t _ima_adpcm_index_table[16] = {
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+
+ int16_t nibble,signed_nibble,diff,step;
+
+ ima_adpcm.last_nibble++;
+ const uint8_t *src_ptr=sdata;
+
+ int ofs = ima_adpcm.last_nibble>>1;
+
+ if (stereo)
+ ofs*=2;
+
+
+ nibble = (ima_adpcm.last_nibble&1)?
+ (src_ptr[ofs]>>4):(src_ptr[ofs]&0xF);
+ step=_ima_adpcm_step_table[ima_adpcm.step_index];
+
+ ima_adpcm.step_index += _ima_adpcm_index_table[nibble];
+ if (ima_adpcm.step_index<0)
+ ima_adpcm.step_index=0;
+ if (ima_adpcm.step_index>88)
+ ima_adpcm.step_index=88;
+
+ /*
+ signed_nibble = (nibble&7) * ((nibble&8)?-1:1);
+ diff = (2 * signed_nibble + 1) * step / 4; */
+
+ diff = step >> 3 ;
+ if (nibble & 1)
+ diff += step >> 2 ;
+ if (nibble & 2)
+ diff += step >> 1 ;
+ if (nibble & 4)
+ diff += step ;
+ if (nibble & 8)
+ diff = -diff ;
+
+ ima_adpcm.predictor+=diff;
+ if (ima_adpcm.predictor<-0x8000)
+ ima_adpcm.predictor=-0x8000;
+ else if (ima_adpcm.predictor>0x7FFF)
+ ima_adpcm.predictor=0x7FFF;
+
+
+ /* store loop if there */
+ if (ima_adpcm.last_nibble==ima_adpcm.loop_pos) {
+
+ ima_adpcm.loop_step_index = ima_adpcm.step_index;
+ ima_adpcm.loop_predictor = ima_adpcm.predictor;
+ }
+
+ }
+
+ float v=ima_adpcm.predictor/32767.0;
+ if (v>max[0])
+ max[0]=v;
+ if (v<min[0])
+ min[0]=v;
+ }
+ max[0]*=0.8;
+ min[0]*=0.8;
+
+ for(int j=0;j<h;j++) {
+ float v = (j/(float)h) * 2.0 - 1.0;
+ uint8_t* imgofs = &imgw[(j*w+i)*3];
+ if (v>min[0] && v<max[0]) {
+ imgofs[0]=255;
+ imgofs[1]=150;
+ imgofs[2]=80;
+ } else {
+ imgofs[0]=0;
+ imgofs[1]=0;
+ imgofs[2]=0;
+ }
+ }
+ }
+ } else {
+ for(int i=0;i<w;i++) {
+ // i trust gcc will optimize this loop
+ float max[2]={-1e10,-1e10};
+ float min[2]={1e10,1e10};
+ int c=stereo?2:1;
+ int from = i*len/w;
+ int to = (i+1)*len/w;
+ if (to>=len)
+ to=len-1;
+
+ if (_16) {
+ const int16_t*src =(const int16_t*)sdata;
+
+ for(int j=0;j<c;j++) {
+
+ for(int k=from;k<=to;k++) {
+
+ float v = src[k*c+j]/32768.0;
+ if (v>max[j])
+ max[j]=v;
+ if (v<min[j])
+ min[j]=v;
+ }
+
+ }
+ } else {
+
+ const int8_t*src =(const int8_t*)sdata;
+
+ for(int j=0;j<c;j++) {
+
+ for(int k=from;k<=to;k++) {
+
+ float v = src[k*c+j]/128.0;
+ if (v>max[j])
+ max[j]=v;
+ if (v<min[j])
+ min[j]=v;
+ }
+
+ }
+ }
+
+ max[0]*=0.8;
+ max[1]*=0.8;
+ min[0]*=0.8;
+ min[1]*=0.8;
+
+ if (!stereo) {
+ for(int j=0;j<h;j++) {
+ float v = (j/(float)h) * 2.0 - 1.0;
+ uint8_t* imgofs = &imgw[(j*w+i)*3];
+ if (v>min[0] && v<max[0]) {
+ imgofs[0]=255;
+ imgofs[1]=150;
+ imgofs[2]=80;
+ } else {
+ imgofs[0]=0;
+ imgofs[1]=0;
+ imgofs[2]=0;
+ }
+ }
+ } else {
+
+ for(int j=0;j<h;j++) {
+
+ int half,ofs;
+ float v;
+ if (j<(h/2)) {
+ half=0;
+ ofs=0;
+ v = (j/(float)(h/2)) * 2.0 - 1.0;
+ } else {
+ half=1;
+ ofs=h/2;
+ v = ((j-(h/2))/(float)(h/2)) * 2.0 - 1.0;
+ }
+
+ uint8_t* imgofs = &imgw[(j*w+i)*3];
+ if (v>min[half] && v<max[half]) {
+ imgofs[0]=255;
+ imgofs[1]=150;
+ imgofs[2]=80;
+ } else {
+ imgofs[0]=0;
+ imgofs[1]=0;
+ imgofs[2]=0;
+ }
+ }
+
+ }
+
+ }
+ }
+
+ imgdata = DVector<uint8_t>::Write();
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture));
+ ptex->create_from_image(Image(w,h,0,Image::FORMAT_RGB,img),0);
+ return ptex;
+
+}
+
+EditorSamplePreviewPlugin::EditorSamplePreviewPlugin() {
+
+
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+bool EditorMeshPreviewPlugin::handles(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"Mesh"); //any Mesh
+}
+
+Ref<Texture> EditorMeshPreviewPlugin::generate(const RES& p_from) {
+
+ Ref<Mesh> mesh = p_from;
+ ERR_FAIL_COND_V(mesh.is_null(),Ref<Texture>());
+
+ VS::get_singleton()->instance_set_base(mesh_instance,mesh->get_rid());
+
+ AABB aabb= mesh->get_aabb();
+ Vector3 ofs = aabb.pos + aabb.size*0.5;
+ aabb.pos-=ofs;
+ Transform xform;
+ xform.basis=Matrix3().rotated(Vector3(0,1,0),Math_PI*0.125);
+ xform.basis = Matrix3().rotated(Vector3(1,0,0),-Math_PI*0.125)*xform.basis;
+ AABB rot_aabb = xform.xform(aabb);
+ float m = MAX(rot_aabb.size.x,rot_aabb.size.y)*0.5;
+ if (m==0)
+ return Ref<Texture>();
+ m=1.0/m;
+ m*=0.5;
+ //print_line("scale: "+rtos(m));
+ xform.basis.scale(Vector3(m,m,m));
+ xform.origin=-xform.basis.xform(ofs); //-ofs*m;
+ xform.origin.z-=rot_aabb.size.z*2;
+ VS::get_singleton()->instance_set_transform(mesh_instance,xform);
+
+
+
+ VS::get_singleton()->viewport_queue_screen_capture(viewport);
+ VS::get_singleton()->viewport_set_render_target_update_mode(viewport,VS::RENDER_TARGET_UPDATE_ONCE); //once used for capture
+// print_line("queue capture!");
+ Image img;
+
+ int timeout=1000;
+ while(timeout) {
+ //print_line("try capture?");
+ OS::get_singleton()->delay_usec(10);
+ img = VS::get_singleton()->viewport_get_screen_capture(viewport);
+ if (!img.empty())
+ break;
+ timeout--;
+ }
+
+ //print_line("captured!");
+ VS::get_singleton()->instance_set_base(mesh_instance,RID());
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+ img.resize(thumbnail_size,thumbnail_size);
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture ));
+ ptex->create_from_image(img,0);
+ return ptex;
+}
+
+EditorMeshPreviewPlugin::EditorMeshPreviewPlugin() {
+
+ scenario = VS::get_singleton()->scenario_create();
+ viewport = VS::get_singleton()->viewport_create();
+ VS::get_singleton()->viewport_set_as_render_target(viewport,true);
+ VS::get_singleton()->viewport_set_render_target_update_mode(viewport,VS::RENDER_TARGET_UPDATE_DISABLED);
+ VS::get_singleton()->viewport_set_scenario(viewport,scenario);
+ VS::ViewportRect vr;
+ vr.x=0;
+ vr.y=0;
+ vr.width=128;
+ vr.height=128;
+ VS::get_singleton()->viewport_set_rect(viewport,vr);
+
+ camera = VS::get_singleton()->camera_create();
+ VS::get_singleton()->viewport_attach_camera(viewport,camera);
+ VS::get_singleton()->camera_set_transform(camera,Transform(Matrix3(),Vector3(0,0,3)));
+// VS::get_singleton()->camera_set_perspective(camera,45,0.1,10);
+ VS::get_singleton()->camera_set_orthogonal(camera,1.0,0.01,1000.0);
+
+ light = VS::get_singleton()->light_create(VS::LIGHT_DIRECTIONAL);
+ light_instance = VS::get_singleton()->instance_create2(light,scenario);
+ VS::get_singleton()->instance_set_transform(light_instance,Transform().looking_at(Vector3(-1,-1,-1),Vector3(0,1,0)));
+
+ light2 = VS::get_singleton()->light_create(VS::LIGHT_DIRECTIONAL);
+ VS::get_singleton()->light_set_color(light2,VS::LIGHT_COLOR_DIFFUSE,Color(0.7,0.7,0.7));
+ VS::get_singleton()->light_set_color(light2,VS::LIGHT_COLOR_SPECULAR,Color(0.0,0.0,0.0));
+ light_instance2 = VS::get_singleton()->instance_create2(light2,scenario);
+
+ VS::get_singleton()->instance_set_transform(light_instance2,Transform().looking_at(Vector3(0,1,0),Vector3(0,0,1)));
+
+// sphere = VS::get_singleton()->mesh_create();
+ mesh_instance = VS::get_singleton()->instance_create();
+ VS::get_singleton()->instance_set_scenario(mesh_instance,scenario);
+
+
+
+}
+
+EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() {
+
+ //VS::get_singleton()->free(sphere);
+ VS::get_singleton()->free(mesh_instance);
+ VS::get_singleton()->free(viewport);
+ VS::get_singleton()->free(light);
+ VS::get_singleton()->free(light_instance);
+ VS::get_singleton()->free(light2);
+ VS::get_singleton()->free(light_instance2);
+ VS::get_singleton()->free(camera);
+ VS::get_singleton()->free(scenario);
+
+}
diff --git a/tools/editor/plugins/editor_preview_plugins.h b/tools/editor/plugins/editor_preview_plugins.h
new file mode 100644
index 0000000000..98071e2a0e
--- /dev/null
+++ b/tools/editor/plugins/editor_preview_plugins.h
@@ -0,0 +1,88 @@
+#ifndef EDITORPREVIEWPLUGINS_H
+#define EDITORPREVIEWPLUGINS_H
+
+#include "tools/editor/editor_resource_preview.h"
+
+class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator {
+public:
+
+ virtual bool handles(const String& p_type) const;
+ virtual Ref<Texture> generate(const RES& p_from);
+
+ EditorTexturePreviewPlugin();
+};
+
+
+class EditorPackedScenePreviewPlugin : public EditorResourcePreviewGenerator {
+
+ Ref<Texture> _gen_from_imd(Ref<ResourceImportMetadata> p_imd);
+public:
+
+ virtual bool handles(const String& p_type) const;
+ virtual Ref<Texture> generate(const RES& p_from);
+ virtual Ref<Texture> generate_from_path(const String& p_path);
+
+ EditorPackedScenePreviewPlugin();
+};
+
+class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator {
+
+ RID scenario;
+ RID sphere;
+ RID sphere_instance;
+ RID viewport;
+ RID light;
+ RID light_instance;
+ RID light2;
+ RID light_instance2;
+ RID camera;
+public:
+
+ virtual bool handles(const String& p_type) const;
+ virtual Ref<Texture> generate(const RES& p_from);
+
+ EditorMaterialPreviewPlugin();
+ ~EditorMaterialPreviewPlugin();
+};
+
+class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator {
+public:
+
+ virtual bool handles(const String& p_type) const;
+ virtual Ref<Texture> generate(const RES& p_from);
+
+ EditorScriptPreviewPlugin();
+};
+
+
+class EditorSamplePreviewPlugin : public EditorResourcePreviewGenerator {
+public:
+
+ virtual bool handles(const String& p_type) const;
+ virtual Ref<Texture> generate(const RES& p_from);
+
+ EditorSamplePreviewPlugin();
+};
+
+
+class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator {
+
+ RID scenario;
+ RID mesh_instance;
+ RID viewport;
+ RID light;
+ RID light_instance;
+ RID light2;
+ RID light_instance2;
+ RID camera;
+public:
+
+ virtual bool handles(const String& p_type) const;
+ virtual Ref<Texture> generate(const RES& p_from);
+
+ EditorMeshPreviewPlugin();
+ ~EditorMeshPreviewPlugin();
+};
+
+
+#endif // EDITORPREVIEWPLUGINS_H
diff --git a/tools/editor/plugins/item_list_editor_plugin.cpp b/tools/editor/plugins/item_list_editor_plugin.cpp
index eb7ab69987..fa261edea3 100644
--- a/tools/editor/plugins/item_list_editor_plugin.cpp
+++ b/tools/editor/plugins/item_list_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -210,6 +210,7 @@ void ItemListEditor::_bind_methods() {
}
bool ItemListEditor::handles(Object *p_object) const {
+ return false;
for(int i=0;i<item_plugins.size();i++) {
if (item_plugins[i]->handles(p_object)) {
return true;
diff --git a/tools/editor/plugins/item_list_editor_plugin.h b/tools/editor/plugins/item_list_editor_plugin.h
index 6b4d26fb45..351dbb800d 100644
--- a/tools/editor/plugins/item_list_editor_plugin.h
+++ b/tools/editor/plugins/item_list_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/plugins/light_occluder_2d_editor_plugin.cpp b/tools/editor/plugins/light_occluder_2d_editor_plugin.cpp
new file mode 100644
index 0000000000..757b5327dd
--- /dev/null
+++ b/tools/editor/plugins/light_occluder_2d_editor_plugin.cpp
@@ -0,0 +1,492 @@
+#include "light_occluder_2d_editor_plugin.h"
+
+#include "canvas_item_editor_plugin.h"
+#include "os/file_access.h"
+#include "tools/editor/editor_settings.h"
+
+void LightOccluder2DEditor::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_READY: {
+
+ button_create->set_icon( get_icon("Edit","EditorIcons"));
+ button_edit->set_icon( get_icon("MovePoint","EditorIcons"));
+ button_edit->set_pressed(true);
+ get_tree()->connect("node_removed",this,"_node_removed");
+ create_poly->connect("confirmed",this,"_create_poly");
+
+ } break;
+ case NOTIFICATION_FIXED_PROCESS: {
+
+
+ } break;
+ }
+
+}
+void LightOccluder2DEditor::_node_removed(Node *p_node) {
+
+ if(p_node==node) {
+ node=NULL;
+ hide();
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+}
+
+
+void LightOccluder2DEditor::_menu_option(int p_option) {
+
+ switch(p_option) {
+
+ case MODE_CREATE: {
+
+ mode=MODE_CREATE;
+ button_create->set_pressed(true);
+ button_edit->set_pressed(false);
+ } break;
+ case MODE_EDIT: {
+
+ mode=MODE_EDIT;
+ button_create->set_pressed(false);
+ button_edit->set_pressed(true);
+ } break;
+
+ }
+}
+
+void LightOccluder2DEditor::_wip_close(bool p_closed) {
+
+ undo_redo->create_action("Create Poly");
+ undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",node->get_occluder_polygon()->get_polygon());
+ undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",wip);
+ undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_closed",node->get_occluder_polygon()->is_closed());
+ undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_closed",p_closed);
+
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ wip.clear();
+ wip_active=false;
+ mode=MODE_EDIT;
+ button_edit->set_pressed(true);
+ button_create->set_pressed(false);
+ edited_point=-1;
+}
+
+bool LightOccluder2DEditor::forward_input_event(const InputEvent& p_event) {
+
+
+ if (!node)
+ return false;
+
+ if (node->get_occluder_polygon().is_null()) {
+ if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) {
+ create_poly->set_text("No OccluderPolygon2D resource on this node.\nCreate and assign one?");
+ create_poly->popup_centered_minsize();
+ }
+ return (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1);
+ }
+ switch(p_event.type) {
+
+ case InputEvent::MOUSE_BUTTON: {
+
+ const InputEventMouseButton &mb=p_event.mouse_button;
+
+ Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+
+
+ Vector2 gpoint = Point2(mb.x,mb.y);
+ Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
+ cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
+
+ Vector<Vector2> poly = Variant(node->get_occluder_polygon()->get_polygon());
+
+ //first check if a point is to be added (segment split)
+ real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8);
+
+ switch(mode) {
+
+
+ case MODE_CREATE: {
+
+ if (mb.button_index==BUTTON_LEFT && mb.pressed) {
+
+
+ if (!wip_active) {
+
+ wip.clear();
+ wip.push_back( cpoint );
+ wip_active=true;
+ edited_point_pos=cpoint;
+ canvas_item_editor->get_viewport_control()->update();
+ edited_point=1;
+ return true;
+ } else {
+
+
+ if (wip.size()>1 && xform.xform(wip[0]).distance_to(gpoint)<grab_treshold) {
+ //wip closed
+ _wip_close(true);
+
+ return true;
+ } else if (wip.size()>1 && xform.xform(wip[wip.size()-1]).distance_to(gpoint)<grab_treshold) {
+ //wip closed
+ _wip_close(false);
+ return true;
+
+ } else {
+
+ wip.push_back( cpoint );
+ edited_point=wip.size();
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+
+ //add wip point
+ }
+ }
+ } else if (mb.button_index==BUTTON_RIGHT && mb.pressed && wip_active) {
+ _wip_close(true);
+ }
+
+
+
+ } break;
+
+ case MODE_EDIT: {
+
+ if (mb.button_index==BUTTON_LEFT) {
+ if (mb.pressed) {
+
+ if (mb.mod.control) {
+
+
+ if (poly.size() < 3) {
+
+ undo_redo->create_action("Edit Poly");
+ undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",poly);
+ poly.push_back(cpoint);
+ undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",poly);
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ //search edges
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 points[2] ={ xform.xform(poly[i]),
+ xform.xform(poly[(i+1)%poly.size()]) };
+
+ Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points);
+ if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2)
+ continue; //not valid to reuse point
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+
+ }
+
+ if (closest_idx>=0) {
+
+ pre_move_edit=poly;
+ poly.insert(closest_idx+1,xform.affine_inverse().xform(closest_pos));
+ edited_point=closest_idx+1;
+ edited_point_pos=xform.affine_inverse().xform(closest_pos);
+ node->get_occluder_polygon()->set_polygon(Variant(poly));
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+ }
+ } else {
+
+ //look for points to move
+
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 cp =xform.xform(poly[i]);
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+ }
+
+ if (closest_idx>=0) {
+
+ pre_move_edit=poly;
+ edited_point=closest_idx;
+ edited_point_pos=xform.affine_inverse().xform(closest_pos);
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+ }
+ }
+ } else {
+
+ if (edited_point!=-1) {
+
+ //apply
+
+ ERR_FAIL_INDEX_V(edited_point,poly.size(),false);
+ poly[edited_point]=edited_point_pos;
+ undo_redo->create_action("Edit Poly");
+ undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",poly);
+ undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",pre_move_edit);
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+
+ edited_point=-1;
+ return true;
+ }
+ }
+ } if (mb.button_index==BUTTON_RIGHT && mb.pressed && edited_point==-1) {
+
+
+
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 cp =xform.xform(poly[i]);
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+ }
+
+ if (closest_idx>=0) {
+
+
+ undo_redo->create_action("Edit Poly (Remove Point)");
+ undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",poly);
+ poly.remove(closest_idx);
+ undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",poly);
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ }
+
+
+
+ } break;
+ }
+
+
+
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+
+ const InputEventMouseMotion &mm=p_event.mouse_motion;
+
+ if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) {
+
+ Vector2 gpoint = Point2(mm.x,mm.y);
+ Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
+ edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint);
+
+ canvas_item_editor->get_viewport_control()->update();
+
+ }
+
+ } break;
+ }
+
+ return false;
+}
+void LightOccluder2DEditor::_canvas_draw() {
+
+ if (!node || !node->get_occluder_polygon().is_valid())
+ return;
+
+ Control *vpc = canvas_item_editor->get_viewport_control();
+
+ Vector<Vector2> poly;
+
+ if (wip_active)
+ poly=wip;
+ else
+ poly=Variant(node->get_occluder_polygon()->get_polygon());
+
+
+ Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+ Ref<Texture> handle= get_icon("EditorHandle","EditorIcons");
+
+ int len = poly.size();
+
+ for(int i=0;i<poly.size();i++) {
+
+
+ Vector2 p,p2;
+ p = i==edited_point ? edited_point_pos : poly[i];
+ if ((wip_active && i==poly.size()-1) || (((i+1)%poly.size())==edited_point))
+ p2=edited_point_pos;
+ else
+ p2 = poly[(i+1)%poly.size()];
+
+ Vector2 point = xform.xform(p);
+ Vector2 next_point = xform.xform(p2);
+
+ Color col=Color(1,0.3,0.1,0.8);
+
+ if (i==poly.size()-1 && (!node->get_occluder_polygon()->is_closed() || wip_active)) {
+
+ } else {
+ vpc->draw_line(point,next_point,col,2);
+ }
+ vpc->draw_texture(handle,point-handle->get_size()*0.5);
+ }
+}
+
+
+
+void LightOccluder2DEditor::edit(Node *p_collision_polygon) {
+
+ if (!canvas_item_editor) {
+ canvas_item_editor=CanvasItemEditor::get_singleton();
+ }
+
+ if (p_collision_polygon) {
+
+ node=p_collision_polygon->cast_to<LightOccluder2D>();
+ if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw");
+ wip.clear();
+ wip_active=false;
+ edited_point=-1;
+ canvas_item_editor->get_viewport_control()->update();
+ } else {
+ node=NULL;
+
+ if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw");
+
+ }
+
+}
+
+void LightOccluder2DEditor::_create_poly() {
+
+ if (!node)
+ return;
+ undo_redo->create_action("Create Occluder Polygon");
+ undo_redo->add_do_method(node,"set_occluder_polygon",Ref<OccluderPolygon2D>(memnew( OccluderPolygon2D)));
+ undo_redo->add_undo_method(node,"set_occluder_polygon",Variant(REF()));
+ undo_redo->commit_action();
+}
+
+void LightOccluder2DEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_menu_option"),&LightOccluder2DEditor::_menu_option);
+ ObjectTypeDB::bind_method(_MD("_canvas_draw"),&LightOccluder2DEditor::_canvas_draw);
+ ObjectTypeDB::bind_method(_MD("_node_removed"),&LightOccluder2DEditor::_node_removed);
+ ObjectTypeDB::bind_method(_MD("_create_poly"),&LightOccluder2DEditor::_create_poly);
+
+}
+
+
+LightOccluder2DEditor::LightOccluder2DEditor(EditorNode *p_editor) {
+
+ node=NULL;
+ canvas_item_editor=NULL;
+ editor=p_editor;
+ undo_redo = editor->get_undo_redo();
+
+ add_child( memnew( VSeparator ));
+ button_create = memnew( ToolButton );
+ add_child(button_create);
+ button_create->connect("pressed",this,"_menu_option",varray(MODE_CREATE));
+ button_create->set_toggle_mode(true);
+ button_create->set_tooltip("Create a new polygon from scratch");
+
+ button_edit = memnew( ToolButton );
+ add_child(button_edit);
+ button_edit->connect("pressed",this,"_menu_option",varray(MODE_EDIT));
+ button_edit->set_toggle_mode(true);
+ button_edit->set_tooltip("Edit existing polygon:\nLMB: Move Point.\nCtrl+LMB: Split Segment.\nRMB: Erase Point.");
+
+ create_poly = memnew( ConfirmationDialog );
+ add_child(create_poly);
+ create_poly->get_ok()->set_text("Create");
+
+
+ //add_constant_override("separation",0);
+
+#if 0
+ options = memnew( MenuButton );
+ add_child(options);
+ options->set_area_as_parent_rect();
+ options->set_text("Polygon");
+ //options->get_popup()->add_item("Parse BBCODE",PARSE_BBCODE);
+ options->get_popup()->connect("item_pressed", this,"_menu_option");
+#endif
+
+ mode = MODE_EDIT;
+ wip_active=false;
+
+}
+
+
+void LightOccluder2DEditorPlugin::edit(Object *p_object) {
+
+ collision_polygon_editor->edit(p_object->cast_to<Node>());
+}
+
+bool LightOccluder2DEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("LightOccluder2D");
+}
+
+void LightOccluder2DEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ collision_polygon_editor->show();
+ } else {
+
+ collision_polygon_editor->hide();
+ collision_polygon_editor->edit(NULL);
+ }
+
+}
+
+LightOccluder2DEditorPlugin::LightOccluder2DEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ collision_polygon_editor = memnew( LightOccluder2DEditor(p_node) );
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor);
+
+ collision_polygon_editor->hide();
+
+
+
+}
+
+
+LightOccluder2DEditorPlugin::~LightOccluder2DEditorPlugin()
+{
+}
+
diff --git a/tools/editor/plugins/light_occluder_2d_editor_plugin.h b/tools/editor/plugins/light_occluder_2d_editor_plugin.h
new file mode 100644
index 0000000000..5fb5631d05
--- /dev/null
+++ b/tools/editor/plugins/light_occluder_2d_editor_plugin.h
@@ -0,0 +1,87 @@
+#ifndef LIGHT_OCCLUDER_2D_EDITOR_PLUGIN_H
+#define LIGHT_OCCLUDER_2D_EDITOR_PLUGIN_H
+
+
+
+#include "tools/editor/editor_plugin.h"
+#include "tools/editor/editor_node.h"
+#include "scene/2d/light_occluder_2d.h"
+#include "scene/gui/tool_button.h"
+#include "scene/gui/button_group.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class CanvasItemEditor;
+
+class LightOccluder2DEditor : public HBoxContainer {
+
+ OBJ_TYPE(LightOccluder2DEditor, HBoxContainer );
+
+ UndoRedo *undo_redo;
+ enum Mode {
+
+ MODE_CREATE,
+ MODE_EDIT,
+
+ };
+
+ Mode mode;
+
+ ToolButton *button_create;
+ ToolButton *button_edit;
+
+ CanvasItemEditor *canvas_item_editor;
+ EditorNode *editor;
+ Panel *panel;
+ LightOccluder2D *node;
+ MenuButton *options;
+
+ int edited_point;
+ Vector2 edited_point_pos;
+ Vector<Vector2> pre_move_edit;
+ Vector<Vector2> wip;
+ bool wip_active;
+
+ ConfirmationDialog *create_poly;
+
+ void _wip_close(bool p_closed);
+ void _canvas_draw();
+ void _menu_option(int p_option);
+ void _create_poly();
+
+protected:
+ void _notification(int p_what);
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+public:
+
+ Vector2 snap_point(const Vector2& p_point) const;
+ bool forward_input_event(const InputEvent& p_event);
+ void edit(Node *p_collision_polygon);
+ LightOccluder2DEditor(EditorNode *p_editor);
+};
+
+class LightOccluder2DEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( LightOccluder2DEditorPlugin, EditorPlugin );
+
+ LightOccluder2DEditor *collision_polygon_editor;
+ EditorNode *editor;
+
+public:
+
+ virtual bool forward_input_event(const InputEvent& p_event) { return collision_polygon_editor->forward_input_event(p_event); }
+
+ virtual String get_name() const { return "LightOccluder2D"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_node);
+ virtual bool handles(Object *p_node) const;
+ virtual void make_visible(bool p_visible);
+
+ LightOccluder2DEditorPlugin(EditorNode *p_node);
+ ~LightOccluder2DEditorPlugin();
+
+};
+
+#endif // LIGHT_OCCLUDER_2D_EDITOR_PLUGIN_H
diff --git a/tools/editor/plugins/mesh_editor_plugin.cpp b/tools/editor/plugins/mesh_editor_plugin.cpp
index a3884f9be4..cea774f94b 100644
--- a/tools/editor/plugins/mesh_editor_plugin.cpp
+++ b/tools/editor/plugins/mesh_editor_plugin.cpp
@@ -33,7 +33,7 @@ void MeshInstanceEditor::_menu_option(int p_option) {
Ref<Mesh> mesh = node->get_mesh();
if (mesh.is_null()) {
err_dialog->set_text("Mesh is empty!");
- err_dialog->popup_centered(Size2(100,50));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -85,7 +85,7 @@ void MeshInstanceEditor::_menu_option(int p_option) {
if (node==get_tree()->get_edited_scene_root()) {
err_dialog->set_text("This doesn't work on scene root!");
- err_dialog->popup_centered(Size2(100,50));
+ err_dialog->popup_centered_minsize();
return;
}
Ref<Shape> shape = mesh->create_trimesh_shape();
@@ -111,7 +111,7 @@ void MeshInstanceEditor::_menu_option(int p_option) {
if (node==get_tree()->get_edited_scene_root()) {
err_dialog->set_text("This doesn't work on scene root!");
- err_dialog->popup_centered(Size2(100,50));
+ err_dialog->popup_centered_minsize();
return;
}
Ref<Shape> shape = mesh->create_convex_shape();
@@ -160,7 +160,7 @@ void MeshInstanceEditor::_menu_option(int p_option) {
} break;
case MENU_OPTION_CREATE_OUTLINE_MESH: {
- outline_dialog->popup_centered(Size2(200,80));
+ outline_dialog->popup_centered(Vector2(200, 90));
} break;
}
@@ -171,7 +171,7 @@ void MeshInstanceEditor::_create_outline_mesh() {
Ref<Mesh> mesh = node->get_mesh();
if (mesh.is_null()) {
err_dialog->set_text("MeshInstance lacks a Mesh!");
- err_dialog->popup_centered(Size2(100,50));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -179,7 +179,7 @@ void MeshInstanceEditor::_create_outline_mesh() {
if (mesho.is_null()) {
err_dialog->set_text("Could not create outline!");
- err_dialog->popup_centered(Size2(100,50));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -212,10 +212,11 @@ MeshInstanceEditor::MeshInstanceEditor() {
options = memnew( MenuButton );
- //add_child(options);
SpatialEditor::get_singleton()->add_control_to_menu_panel(options);
options->set_text("Mesh");
+ options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MeshInstance","EditorIcons"));
+
options->get_popup()->add_item("Create Trimesh Static Body",MENU_OPTION_CREATE_STATIC_TRIMESH_BODY);
options->get_popup()->add_item("Create Convex Static Body",MENU_OPTION_CREATE_STATIC_CONVEX_BODY);
options->get_popup()->add_separator();
@@ -229,17 +230,26 @@ MeshInstanceEditor::MeshInstanceEditor() {
options->get_popup()->connect("item_pressed", this,"_menu_option");
outline_dialog = memnew( ConfirmationDialog );
- outline_dialog->set_title("Outline Size: ");
+ outline_dialog->set_title("Create Outline Mesh");
+ outline_dialog->get_ok()->set_text("Create");
+
+ VBoxContainer *outline_dialog_vbc = memnew( VBoxContainer );
+ outline_dialog->add_child(outline_dialog_vbc);
+ outline_dialog->set_child_rect(outline_dialog_vbc);
+
outline_size = memnew( SpinBox );
outline_size->set_min(0.001);
outline_size->set_max(1024);
outline_size->set_step(0.001);
outline_size->set_val(0.05);
- outline_dialog->add_child(outline_size);
- outline_dialog->set_child_rect(outline_size);
+ outline_dialog_vbc->add_margin_child("Outline Size:",outline_size);
+
add_child(outline_dialog);
outline_dialog->connect("confirmed",this,"_create_outline_mesh");
+ err_dialog = memnew( AcceptDialog );
+ add_child(err_dialog);
+
}
diff --git a/tools/editor/plugins/multimesh_editor_plugin.cpp b/tools/editor/plugins/multimesh_editor_plugin.cpp
index b2b6cbe9b7..a5c823f8bd 100644
--- a/tools/editor/plugins/multimesh_editor_plugin.cpp
+++ b/tools/editor/plugins/multimesh_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,8 +29,7 @@
#include "multimesh_editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/3d/mesh_instance.h"
-
-
+#include "spatial_editor_plugin.h"
void MultiMeshEditor::_node_removed(Node *p_node) {
@@ -57,13 +56,13 @@ void MultiMeshEditor::_populate() {
if (multimesh.is_null()) {
err_dialog->set_text("No mesh source specified (and no MultiMesh set in node).");
- err_dialog->popup_centered(Size2(300,100));
+ err_dialog->popup_centered_minsize();
return;
}
if (multimesh->get_mesh().is_null()) {
err_dialog->set_text("No mesh source specified (and MultiMesh contains no Mesh).");
- err_dialog->popup_centered(Size2(300,100));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -75,7 +74,7 @@ void MultiMeshEditor::_populate() {
if (!ms_node) {
err_dialog->set_text("Mesh source is invalid (Invalid Path).");
- err_dialog->popup_centered(Size2(300,100));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -84,7 +83,7 @@ void MultiMeshEditor::_populate() {
if (!ms_instance) {
err_dialog->set_text("Mesh source is invalid (Not a MeshInstance).");
- err_dialog->popup_centered(Size2(300,100));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -93,7 +92,7 @@ void MultiMeshEditor::_populate() {
if (mesh.is_null()) {
err_dialog->set_text("Mesh source is invalid (Contains no Mesh resource).");
- err_dialog->popup_centered(Size2(300,100));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -102,7 +101,7 @@ void MultiMeshEditor::_populate() {
if (surface_source->get_text()=="") {
err_dialog->set_text("No surface source specified.");
- err_dialog->popup_centered(Size2(300,100));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -111,7 +110,7 @@ void MultiMeshEditor::_populate() {
if (!ss_node) {
err_dialog->set_text("Surface source is invalid (Invalid Path).");
- err_dialog->popup_centered(Size2(300,100));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -120,7 +119,7 @@ void MultiMeshEditor::_populate() {
if (!ss_instance) {
err_dialog->set_text("Surface source is invalid (Not Geometry).");
- err_dialog->popup_centered(Size2(300,100));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -131,7 +130,7 @@ void MultiMeshEditor::_populate() {
if (geometry.size()==0) {
err_dialog->set_text("Surface source is invalid (No Faces).");
- err_dialog->popup_centered(Size2(300,100));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -290,7 +289,7 @@ void MultiMeshEditor::_menu_option(int p_option) {
_last_pp_node=node;
}
- populate_dialog->popup_centered(Size2(250,395));
+ populate_dialog->popup_centered(Size2(250,380));
} break;
}
@@ -299,14 +298,14 @@ void MultiMeshEditor::_menu_option(int p_option) {
void MultiMeshEditor::edit(MultiMeshInstance *p_multimesh) {
- node=p_multimesh;
+ node=p_multimesh;
}
void MultiMeshEditor::_browse(bool p_source) {
browsing_source=p_source;
- std->get_tree()->set_marked(node,false);
+ std->get_scene_tree()->set_marked(node,false);
std->popup_centered_ratio();
if (p_source)
std->set_title("Select a Source Mesh:");
@@ -326,10 +325,11 @@ MultiMeshEditor::MultiMeshEditor() {
options = memnew( MenuButton );
- add_child(options);
- options->set_area_as_parent_rect();
-
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(options);
+
options->set_text("MultiMesh");
+ options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MultiMeshInstance","EditorIcons"));
+
options->get_popup()->add_item("Populate Surface");
options->get_popup()->connect("item_pressed", this,"_menu_option");
@@ -341,7 +341,6 @@ MultiMeshEditor::MultiMeshEditor() {
populate_dialog->add_child(vbc);
populate_dialog->set_child_rect(vbc);
-
HBoxContainer *hbc = memnew( HBoxContainer );
surface_source = memnew( LineEdit );
@@ -372,12 +371,12 @@ MultiMeshEditor::MultiMeshEditor() {
populate_axis->select(2);
vbc->add_margin_child("Mesh Up Axis:",populate_axis);
- populate_rotate_random = memnew( HScrollBar );
+ populate_rotate_random = memnew( HSlider );
populate_rotate_random->set_max(1);
populate_rotate_random->set_step(0.01);
vbc->add_margin_child("Random Rotation:",populate_rotate_random);
- populate_tilt_random = memnew( HScrollBar );
+ populate_tilt_random = memnew( HSlider );
populate_tilt_random->set_max(1);
populate_tilt_random->set_step(0.01);
vbc->add_margin_child("Random Tilt:",populate_tilt_random);
@@ -415,8 +414,7 @@ MultiMeshEditor::MultiMeshEditor() {
std->connect("selected",this,"_browsed");
_last_pp_node=NULL;
- //options->set_anchor(MARGIN_LEFT,Control::ANCHOR_END);
- //options->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END);
+
err_dialog = memnew( AcceptDialog );
add_child(err_dialog);
}
@@ -435,10 +433,10 @@ bool MultiMeshEditorPlugin::handles(Object *p_object) const {
void MultiMeshEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
- multimesh_editor->show();
+ multimesh_editor->options->show();
} else {
- multimesh_editor->hide();
+ multimesh_editor->options->hide();
multimesh_editor->edit(NULL);
}
@@ -450,16 +448,7 @@ MultiMeshEditorPlugin::MultiMeshEditorPlugin(EditorNode *p_node) {
multimesh_editor = memnew( MultiMeshEditor );
editor->get_viewport()->add_child(multimesh_editor);
-// multimesh_editor->set_anchor(MARGIN_LEFT,Control::ANCHOR_END);
-// multimesh_editor->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END);
- multimesh_editor->set_margin(MARGIN_LEFT,253);
- multimesh_editor->set_margin(MARGIN_RIGHT,310);
- multimesh_editor->set_margin(MARGIN_TOP,0);
- multimesh_editor->set_margin(MARGIN_BOTTOM,10);
-
-
-
- multimesh_editor->hide();
+ multimesh_editor->options->hide();
}
diff --git a/tools/editor/plugins/multimesh_editor_plugin.h b/tools/editor/plugins/multimesh_editor_plugin.h
index a4d5f9bd30..edc3dfd55f 100644
--- a/tools/editor/plugins/multimesh_editor_plugin.h
+++ b/tools/editor/plugins/multimesh_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -42,14 +42,14 @@ class MultiMeshEditor : public Control {
OBJ_TYPE(MultiMeshEditor, Control );
+friend class MultiMeshEditorPlugin;
AcceptDialog *err_dialog;
-
+ MenuButton * options;
MultiMeshInstance *_last_pp_node;
bool browsing_source;
Panel *panel;
- MenuButton * options;
MultiMeshInstance *node;
LineEdit *surface_source;
@@ -59,8 +59,8 @@ class MultiMeshEditor : public Control {
ConfirmationDialog *populate_dialog;
OptionButton *populate_axis;
- HScrollBar *populate_rotate_random;
- HScrollBar *populate_tilt_random;
+ HSlider *populate_rotate_random;
+ HSlider *populate_tilt_random;
SpinBox *populate_scale_random;
SpinBox *populate_scale;
SpinBox *populate_amount;
diff --git a/tools/editor/plugins/navigation_polygon_editor_plugin.cpp b/tools/editor/plugins/navigation_polygon_editor_plugin.cpp
new file mode 100644
index 0000000000..fa1f614413
--- /dev/null
+++ b/tools/editor/plugins/navigation_polygon_editor_plugin.cpp
@@ -0,0 +1,540 @@
+#include "navigation_polygon_editor_plugin.h"
+
+#include "canvas_item_editor_plugin.h"
+#include "os/file_access.h"
+#include "tools/editor/editor_settings.h"
+
+void NavigationPolygonEditor::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_READY: {
+
+ button_create->set_icon( get_icon("Edit","EditorIcons"));
+ button_edit->set_icon( get_icon("MovePoint","EditorIcons"));
+ button_edit->set_pressed(true);
+ get_tree()->connect("node_removed",this,"_node_removed");
+ create_nav->connect("confirmed",this,"_create_nav");
+
+ } break;
+ case NOTIFICATION_FIXED_PROCESS: {
+
+
+ } break;
+ }
+
+}
+void NavigationPolygonEditor::_node_removed(Node *p_node) {
+
+ if(p_node==node) {
+ node=NULL;
+ hide();
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+}
+
+void NavigationPolygonEditor::_create_nav() {
+
+ if (!node)
+ return;
+
+ undo_redo->create_action("Create Navigation Polygon");
+ undo_redo->add_do_method(node,"set_navigation_polygon",Ref<NavigationPolygon>(memnew( NavigationPolygon)));
+ undo_redo->add_undo_method(node,"set_navigation_polygon",Variant(REF()));
+ undo_redo->commit_action();
+}
+
+void NavigationPolygonEditor::_menu_option(int p_option) {
+
+ switch(p_option) {
+
+ case MODE_CREATE: {
+
+ mode=MODE_CREATE;
+ button_create->set_pressed(true);
+ button_edit->set_pressed(false);
+ } break;
+ case MODE_EDIT: {
+
+ mode=MODE_EDIT;
+ button_create->set_pressed(false);
+ button_edit->set_pressed(true);
+ } break;
+
+ }
+}
+
+void NavigationPolygonEditor::_wip_close() {
+
+
+ if (wip.size()>=3) {
+
+ undo_redo->create_action("Create Poly");
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"remove_outline",node->get_navigation_polygon()->get_outline_count());
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"add_outline",wip);
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ mode=MODE_EDIT;
+ button_edit->set_pressed(true);
+ button_create->set_pressed(false);
+ }
+
+ wip.clear();
+ wip_active=false;
+ edited_point=-1;
+}
+
+bool NavigationPolygonEditor::forward_input_event(const InputEvent& p_event) {
+
+
+ if (!node)
+ return false;
+
+ if (node->get_navigation_polygon().is_null()) {
+ if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) {
+ create_nav->set_text("No NavigationPolygon resource on this node.\nCreate and assign one?");
+ create_nav->popup_centered_minsize();
+ }
+ return (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1);;
+ }
+
+
+ switch(p_event.type) {
+
+ case InputEvent::MOUSE_BUTTON: {
+
+ const InputEventMouseButton &mb=p_event.mouse_button;
+
+ Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+
+
+ Vector2 gpoint = Point2(mb.x,mb.y);
+ Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
+ cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
+
+
+
+ //first check if a point is to be added (segment split)
+ real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8);
+
+ switch(mode) {
+
+
+ case MODE_CREATE: {
+
+ if (mb.button_index==BUTTON_LEFT && mb.pressed) {
+
+
+ if (!wip_active) {
+
+ wip.clear();
+ wip.push_back( cpoint );
+ wip_active=true;
+ edited_point_pos=cpoint;
+ edited_outline=-1;
+ canvas_item_editor->get_viewport_control()->update();
+ edited_point=1;
+ return true;
+ } else {
+
+
+ if (wip.size()>1 && xform.xform(wip[0]).distance_to(gpoint)<grab_treshold) {
+ //wip closed
+ _wip_close();
+
+ return true;
+ } else {
+
+ wip.push_back( cpoint );
+ edited_point=wip.size();
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+
+ //add wip point
+ }
+ }
+ } else if (mb.button_index==BUTTON_RIGHT && mb.pressed && wip_active) {
+ _wip_close();
+ }
+
+
+
+ } break;
+
+ case MODE_EDIT: {
+
+ if (mb.button_index==BUTTON_LEFT) {
+ if (mb.pressed) {
+
+ if (mb.mod.control) {
+
+
+ //search edges
+ int closest_outline=-1;
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+
+ for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) {
+
+
+ DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j);
+
+ int pc=points.size();
+ DVector<Vector2>::Read poly=points.read();
+
+ for(int i=0;i<pc;i++) {
+
+ Vector2 points[2] ={ xform.xform(poly[i]),
+ xform.xform(poly[(i+1)%pc]) };
+
+ Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points);
+ if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2)
+ continue; //not valid to reuse point
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_outline=j;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+
+ }
+ }
+
+ if (closest_idx>=0) {
+
+ pre_move_edit=node->get_navigation_polygon()->get_outline(closest_outline);
+ DVector<Point2> poly = pre_move_edit;
+ poly.insert(closest_idx+1,xform.affine_inverse().xform(closest_pos));
+ edited_point=closest_idx+1;
+ edited_outline=closest_outline;
+ edited_point_pos=xform.affine_inverse().xform(closest_pos);
+ node->get_navigation_polygon()->set_outline(closest_outline,poly);
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+ }
+ } else {
+
+ //look for points to move
+ int closest_outline=-1;
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+
+ for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) {
+
+
+ DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j);
+
+ int pc=points.size();
+ DVector<Vector2>::Read poly=points.read();
+
+ for(int i=0;i<pc;i++) {
+
+
+ Vector2 cp =xform.xform(poly[i]);
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_outline=j;
+ closest_idx=i;
+ }
+ }
+ }
+
+ if (closest_idx>=0) {
+
+ pre_move_edit=node->get_navigation_polygon()->get_outline(closest_outline);
+ edited_point=closest_idx;
+ edited_outline=closest_outline;
+ edited_point_pos=xform.affine_inverse().xform(closest_pos);
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+ }
+ }
+ } else {
+
+ if (edited_point!=-1) {
+
+ //apply
+
+ DVector<Vector2> poly = node->get_navigation_polygon()->get_outline(edited_outline);
+ ERR_FAIL_INDEX_V(edited_point,poly.size(),false);
+ poly.set(edited_point,edited_point_pos);
+ undo_redo->create_action("Edit Poly");
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"set_outline",edited_outline,poly);
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"set_outline",edited_outline,pre_move_edit);
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+
+ edited_point=-1;
+ return true;
+ }
+ }
+ } if (mb.button_index==BUTTON_RIGHT && mb.pressed && edited_point==-1) {
+
+ int closest_outline=-1;
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+
+ for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) {
+
+
+ DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j);
+
+ int pc=points.size();
+ DVector<Vector2>::Read poly=points.read();
+
+ for(int i=0;i<pc;i++) {
+
+
+ Vector2 cp =xform.xform(poly[i]);
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_outline=j;
+ closest_idx=i;
+ }
+ }
+ }
+
+ if (closest_idx>=0) {
+
+
+ DVector<Vector2> poly = node->get_navigation_polygon()->get_outline(closest_outline);
+
+ if (poly.size()>3) {
+ undo_redo->create_action("Edit Poly (Remove Point)");
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"set_outline",closest_outline,poly);
+ poly.remove(closest_idx);
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"set_outline",closest_outline,poly);
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ } else {
+
+ undo_redo->create_action("Remove Poly And Point");
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"add_outline_at_index",poly,closest_outline);
+ poly.remove(closest_idx);
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"remove_outline",closest_outline);
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+
+ }
+ return true;
+ }
+ }
+
+
+
+ } break;
+ }
+
+
+
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+
+ const InputEventMouseMotion &mm=p_event.mouse_motion;
+
+ if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) {
+
+ Vector2 gpoint = Point2(mm.x,mm.y);
+ Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
+ edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint);
+
+ canvas_item_editor->get_viewport_control()->update();
+
+ }
+
+ } break;
+ }
+
+ return false;
+}
+void NavigationPolygonEditor::_canvas_draw() {
+
+ if (!node)
+ return;
+
+ Control *vpc = canvas_item_editor->get_viewport_control();
+ if (node->get_navigation_polygon().is_null())
+ return;
+
+ Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+ Ref<Texture> handle= get_icon("EditorHandle","EditorIcons");
+
+
+
+ for(int j=-1;j<node->get_navigation_polygon()->get_outline_count();j++) {
+ Vector<Vector2> poly;
+
+ if (wip_active && j==edited_outline) {
+ poly=wip;
+ } else {
+ if (j==-1)
+ continue;
+ poly = Variant(node->get_navigation_polygon()->get_outline(j));
+ }
+
+ int len = poly.size();
+
+ for(int i=0;i<poly.size();i++) {
+
+
+ Vector2 p,p2;
+ p = (j==edited_outline && i==edited_point) ? edited_point_pos : poly[i];
+ if (j==edited_outline && ((wip_active && i==poly.size()-1) || (((i+1)%poly.size())==edited_point)))
+ p2=edited_point_pos;
+ else
+ p2 = poly[(i+1)%poly.size()];
+
+ Vector2 point = xform.xform(p);
+ Vector2 next_point = xform.xform(p2);
+
+ Color col=Color(1,0.3,0.1,0.8);
+ vpc->draw_line(point,next_point,col,2);
+ vpc->draw_texture(handle,point-handle->get_size()*0.5);
+ }
+ }
+}
+
+
+
+void NavigationPolygonEditor::edit(Node *p_collision_polygon) {
+
+ if (!canvas_item_editor) {
+ canvas_item_editor=CanvasItemEditor::get_singleton();
+ }
+
+ if (p_collision_polygon) {
+
+ node=p_collision_polygon->cast_to<NavigationPolygonInstance>();
+ if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw");
+ wip.clear();
+ wip_active=false;
+ edited_point=-1;
+ canvas_item_editor->get_viewport_control()->update();
+
+ } else {
+ node=NULL;
+
+ if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw");
+
+ }
+
+}
+
+void NavigationPolygonEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_menu_option"),&NavigationPolygonEditor::_menu_option);
+ ObjectTypeDB::bind_method(_MD("_canvas_draw"),&NavigationPolygonEditor::_canvas_draw);
+ ObjectTypeDB::bind_method(_MD("_node_removed"),&NavigationPolygonEditor::_node_removed);
+ ObjectTypeDB::bind_method(_MD("_create_nav"),&NavigationPolygonEditor::_create_nav);
+
+}
+
+NavigationPolygonEditor::NavigationPolygonEditor(EditorNode *p_editor) {
+ node=NULL;
+ canvas_item_editor=NULL;
+ editor=p_editor;
+ undo_redo = editor->get_undo_redo();
+
+ add_child( memnew( VSeparator ));
+ button_create = memnew( ToolButton );
+ add_child(button_create);
+ button_create->connect("pressed",this,"_menu_option",varray(MODE_CREATE));
+ button_create->set_toggle_mode(true);
+ button_create->set_tooltip("Create a new polygon from scratch");
+
+ button_edit = memnew( ToolButton );
+ add_child(button_edit);
+ button_edit->connect("pressed",this,"_menu_option",varray(MODE_EDIT));
+ button_edit->set_toggle_mode(true);
+ button_edit->set_tooltip("Edit existing polygon:\nLMB: Move Point.\nCtrl+LMB: Split Segment.\nRMB: Erase Point.");
+ create_nav = memnew( ConfirmationDialog );
+ add_child(create_nav);
+ create_nav->get_ok()->set_text("Create");
+
+
+ //add_constant_override("separation",0);
+
+#if 0
+ options = memnew( MenuButton );
+ add_child(options);
+ options->set_area_as_parent_rect();
+ options->set_text("Polygon");
+ //options->get_popup()->add_item("Parse BBCODE",PARSE_BBCODE);
+ options->get_popup()->connect("item_pressed", this,"_menu_option");
+#endif
+
+ mode = MODE_EDIT;
+ wip_active=false;
+ edited_outline=-1;
+
+}
+
+
+void NavigationPolygonEditorPlugin::edit(Object *p_object) {
+
+ collision_polygon_editor->edit(p_object->cast_to<Node>());
+}
+
+bool NavigationPolygonEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("NavigationPolygonInstance");
+}
+
+void NavigationPolygonEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ collision_polygon_editor->show();
+ } else {
+
+ collision_polygon_editor->hide();
+ collision_polygon_editor->edit(NULL);
+ }
+
+}
+
+NavigationPolygonEditorPlugin::NavigationPolygonEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ collision_polygon_editor = memnew( NavigationPolygonEditor(p_node) );
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor);
+
+ collision_polygon_editor->hide();
+
+
+
+}
+
+
+NavigationPolygonEditorPlugin::~NavigationPolygonEditorPlugin()
+{
+}
+
diff --git a/tools/editor/plugins/navigation_polygon_editor_plugin.h b/tools/editor/plugins/navigation_polygon_editor_plugin.h
new file mode 100644
index 0000000000..f742cb011d
--- /dev/null
+++ b/tools/editor/plugins/navigation_polygon_editor_plugin.h
@@ -0,0 +1,90 @@
+#ifndef NAVIGATIONPOLYGONEDITORPLUGIN_H
+#define NAVIGATIONPOLYGONEDITORPLUGIN_H
+
+
+
+#include "tools/editor/editor_plugin.h"
+#include "tools/editor/editor_node.h"
+#include "scene/2d/navigation_polygon.h"
+#include "scene/gui/tool_button.h"
+#include "scene/gui/button_group.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class CanvasItemEditor;
+
+class NavigationPolygonEditor : public HBoxContainer {
+
+ OBJ_TYPE(NavigationPolygonEditor, HBoxContainer );
+
+ UndoRedo *undo_redo;
+ enum Mode {
+
+ MODE_CREATE,
+ MODE_EDIT,
+
+ };
+
+ Mode mode;
+
+ ToolButton *button_create;
+ ToolButton *button_edit;
+
+ ConfirmationDialog *create_nav;
+
+ CanvasItemEditor *canvas_item_editor;
+ EditorNode *editor;
+ Panel *panel;
+ NavigationPolygonInstance *node;
+ MenuButton *options;
+
+ int edited_outline;
+ int edited_point;
+ Vector2 edited_point_pos;
+ DVector<Vector2> pre_move_edit;
+ Vector<Vector2> wip;
+ bool wip_active;
+
+
+ void _wip_close();
+ void _canvas_draw();
+ void _create_nav();
+
+ void _menu_option(int p_option);
+
+protected:
+ void _notification(int p_what);
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+public:
+
+ bool forward_input_event(const InputEvent& p_event);
+ void edit(Node *p_collision_polygon);
+ NavigationPolygonEditor(EditorNode *p_editor);
+};
+
+class NavigationPolygonEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( NavigationPolygonEditorPlugin, EditorPlugin );
+
+ NavigationPolygonEditor *collision_polygon_editor;
+ EditorNode *editor;
+
+public:
+
+ virtual bool forward_input_event(const InputEvent& p_event) { return collision_polygon_editor->forward_input_event(p_event); }
+
+ virtual String get_name() const { return "NavigationPolygonInstance"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_node);
+ virtual bool handles(Object *p_node) const;
+ virtual void make_visible(bool p_visible);
+
+ NavigationPolygonEditorPlugin(EditorNode *p_node);
+ ~NavigationPolygonEditorPlugin();
+
+};
+
+
+#endif // NAVIGATIONPOLYGONEDITORPLUGIN_H
diff --git a/tools/editor/plugins/particles_2d_editor_plugin.cpp b/tools/editor/plugins/particles_2d_editor_plugin.cpp
index a7adfcd172..dadfa8bfdc 100644
--- a/tools/editor/plugins/particles_2d_editor_plugin.cpp
+++ b/tools/editor/plugins/particles_2d_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -146,6 +146,7 @@ void Particles2DEditorPlugin::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_TREE) {
menu->get_popup()->connect("item_pressed",this,"_menu_callback");
+ menu->set_icon(menu->get_popup()->get_icon("Particles2D","EditorIcons"));
file->connect("file_selected",this,"_file_selected");
}
}
@@ -172,14 +173,14 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) {
menu->get_popup()->add_item("Clear Emission Mask",MENU_CLEAR_EMISSION_MASK);
menu->set_text("Particles");
- file = memnew(FileDialog);
+ file = memnew(EditorFileDialog);
add_child(file);
List<String> ext;
ImageLoader::get_recognized_extensions(&ext);
for(List<String>::Element *E=ext.front();E;E=E->next()) {
file->add_filter("*."+E->get()+"; "+E->get().to_upper());
}
- file->set_mode(FileDialog::MODE_OPEN_FILE);
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
CanvasItemEditor::get_singleton()->add_control_to_menu_panel(menu);
epoints = memnew( SpinBox );
epoints->set_min(1);
diff --git a/tools/editor/plugins/particles_2d_editor_plugin.h b/tools/editor/plugins/particles_2d_editor_plugin.h
index b824774d0e..dba0bb4dae 100644
--- a/tools/editor/plugins/particles_2d_editor_plugin.h
+++ b/tools/editor/plugins/particles_2d_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -26,57 +26,57 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef PARTICLES_2D_EDITOR_PLUGIN_H
-#define PARTICLES_2D_EDITOR_PLUGIN_H
-
-#include "tools/editor/editor_plugin.h"
-#include "tools/editor/editor_node.h"
-#include "scene/2d/collision_polygon_2d.h"
-
-#include "scene/gui/separator.h"
-#include "scene/gui/file_dialog.h"
-#include "scene/2d/particles_2d.h"
-
-class Particles2DEditorPlugin : public EditorPlugin {
-
- OBJ_TYPE( Particles2DEditorPlugin, EditorPlugin );
-
- enum {
-
- MENU_LOAD_EMISSION_MASK,
- MENU_CLEAR_EMISSION_MASK
- };
-
-
- FileDialog *file;
- EditorNode *editor;
-
- MenuButton *menu;
-
- VSeparator *sep;
- Particles2D *particles;
- SpinBox *epoints;
-
- UndoRedo *undo_redo;
- void _file_selected(const String& p_file);
- void _menu_callback(int p_idx);
-protected:
- void _notification(int p_what);
- static void _bind_methods();
-
-public:
-
-
- virtual String get_name() const { return "Particles2D"; }
- bool has_main_screen() const { return false; }
- virtual void edit(Object *p_node);
- virtual bool handles(Object *p_node) const;
- virtual void make_visible(bool p_visible);
-
- Particles2DEditorPlugin(EditorNode *p_node);
- ~Particles2DEditorPlugin();
-
-};
-
-
-#endif // PARTICLES_2D_EDITOR_PLUGIN_H
+#ifndef PARTICLES_2D_EDITOR_PLUGIN_H
+#define PARTICLES_2D_EDITOR_PLUGIN_H
+
+#include "tools/editor/editor_plugin.h"
+#include "tools/editor/editor_node.h"
+#include "scene/2d/collision_polygon_2d.h"
+
+#include "scene/gui/separator.h"
+#include "scene/gui/file_dialog.h"
+#include "scene/2d/particles_2d.h"
+
+class Particles2DEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( Particles2DEditorPlugin, EditorPlugin );
+
+ enum {
+
+ MENU_LOAD_EMISSION_MASK,
+ MENU_CLEAR_EMISSION_MASK
+ };
+
+
+ EditorFileDialog *file;
+ EditorNode *editor;
+
+ MenuButton *menu;
+
+ VSeparator *sep;
+ Particles2D *particles;
+ SpinBox *epoints;
+
+ UndoRedo *undo_redo;
+ void _file_selected(const String& p_file);
+ void _menu_callback(int p_idx);
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+
+ virtual String get_name() const { return "Particles2D"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_node);
+ virtual bool handles(Object *p_node) const;
+ virtual void make_visible(bool p_visible);
+
+ Particles2DEditorPlugin(EditorNode *p_node);
+ ~Particles2DEditorPlugin();
+
+};
+
+
+#endif // PARTICLES_2D_EDITOR_PLUGIN_H
diff --git a/tools/editor/plugins/particles_editor_plugin.cpp b/tools/editor/plugins/particles_editor_plugin.cpp
index ebb45bc316..5c84d9a86a 100644
--- a/tools/editor/plugins/particles_editor_plugin.cpp
+++ b/tools/editor/plugins/particles_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -58,7 +58,7 @@ void ParticlesEditor::_node_selected(const NodePath& p_path){
if (!vi) {
err_dialog->set_text("Node does not contain geometry.");
- err_dialog->popup_centered(Size2(300,100));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -67,7 +67,7 @@ void ParticlesEditor::_node_selected(const NodePath& p_path){
if (geometry.size()==0) {
err_dialog->set_text("Node does not contain geometry (faces).");
- err_dialog->popup_centered(Size2(300,100));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -111,6 +111,7 @@ void ParticlesEditor::_populate() {
void ParticlesEditor::_notification(int p_notification) {
if (p_notification==NOTIFICATION_ENTER_TREE) {
+ options->set_icon(options->get_popup()->get_icon("Particles","EditorIcons"));
}
}
@@ -219,7 +220,7 @@ void ParticlesEditor::_generate_emission_points() {
if (!triangle_area_map.size() || area_accum==0) {
err_dialog->set_text("Faces contain no area!");
- err_dialog->popup_centered(Size2(300,100));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -249,7 +250,7 @@ void ParticlesEditor::_generate_emission_points() {
if (gcount==0) {
err_dialog->set_text("No Faces!");
- err_dialog->popup_centered(Size2(300,100));
+ err_dialog->popup_centered_minsize();
return;
}
@@ -394,7 +395,7 @@ ParticlesEditor::ParticlesEditor() {
add_child(err_dialog);
- emission_file_dialog = memnew( FileDialog );
+ emission_file_dialog = memnew( EditorFileDialog );
add_child(emission_file_dialog);
emission_file_dialog->connect("file_selected",this,"_resource_seleted");
emission_tree_dialog = memnew( SceneTreeDialog );
@@ -410,7 +411,7 @@ ParticlesEditor::ParticlesEditor() {
emission_file_dialog->add_filter("*."+extensions[i]+" ; "+extensions[i].to_upper());
}
- emission_file_dialog->set_mode(FileDialog::MODE_OPEN_FILE);
+ emission_file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
//options->set_anchor(MARGIN_LEFT,Control::ANCHOR_END);
//options->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END);
diff --git a/tools/editor/plugins/particles_editor_plugin.h b/tools/editor/plugins/particles_editor_plugin.h
index 3e4b0f73aa..92756af1f6 100644
--- a/tools/editor/plugins/particles_editor_plugin.h
+++ b/tools/editor/plugins/particles_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -48,7 +48,7 @@ class ParticlesEditor : public Control {
Particles *node;
- FileDialog *emission_file_dialog;
+ EditorFileDialog *emission_file_dialog;
SceneTreeDialog *emission_tree_dialog;
ConfirmationDialog *err_dialog;
diff --git a/tools/editor/plugins/path_2d_editor_plugin.cpp b/tools/editor/plugins/path_2d_editor_plugin.cpp
index 33ea5f3588..d037adc555 100644
--- a/tools/editor/plugins/path_2d_editor_plugin.cpp
+++ b/tools/editor/plugins/path_2d_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -62,17 +62,6 @@ void Path2DEditor::_node_removed(Node *p_node) {
}
-Vector2 Path2DEditor::snap_point(const Vector2& p_point) const {
-
- if (canvas_item_editor->is_snap_active()) {
-
- return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap());
-
- } else {
- return p_point;
- }
-}
-
bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
if (!node)
@@ -93,8 +82,8 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
Vector2 gpoint = Point2(mb.x,mb.y);
- Vector2 cpoint = !mb.mod.alt? snap_point(xform.affine_inverse().xform(gpoint))
- : node->get_global_transform().affine_inverse().xform( snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) );
+ Vector2 cpoint = !mb.mod.alt? canvas_item_editor->snap_point(xform.affine_inverse().xform(gpoint))
+ : node->get_global_transform().affine_inverse().xform( canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) );
//first check if a point is to be added (segment split)
real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8);
@@ -195,7 +184,7 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
Ref<Curve2D> curve = node->get_curve();
- Vector2 new_pos = moving_from + xform.basis_xform( gpoint - moving_screen_from );
+ Vector2 new_pos = moving_from + xform.affine_inverse().basis_xform(gpoint - moving_screen_from);
switch(action) {
case ACTION_MOVING_POINT: {
@@ -250,9 +239,9 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
if (!wip_active) {
wip.clear();
- wip.push_back( snap_point(cpoint) );
+ wip.push_back( canvas_item_editor->snap_point(cpoint) );
wip_active=true;
- edited_point_pos=snap_point(cpoint);
+ edited_point_pos=canvas_item_editor->snap_point(cpoint);
canvas_item_editor->update();
edited_point=1;
return true;
@@ -265,7 +254,7 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
return true;
} else {
- wip.push_back( snap_point(cpoint) );
+ wip.push_back( canvas_item_editor->snap_point(cpoint) );
edited_point=wip.size();
canvas_item_editor->update();
return true;
@@ -327,9 +316,9 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
if (closest_idx>=0) {
pre_move_edit=poly;
- poly.insert(closest_idx+1,snap_point(xform.affine_inverse().xform(closest_pos)));
+ poly.insert(closest_idx+1,canvas_item_editor->snap_point(xform.affine_inverse().xform(closest_pos)));
edited_point=closest_idx+1;
- edited_point_pos=snap_point(xform.affine_inverse().xform(closest_pos));
+ edited_point_pos=canvas_item_editor->snap_point(xform.affine_inverse().xform(closest_pos));
node->set_polygon(poly);
canvas_item_editor->update();
return true;
@@ -434,12 +423,12 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
Vector2 gpoint = Point2(mm.x,mm.y);
- Vector2 cpoint = !mm.mod.alt? snap_point(xform.affine_inverse().xform(gpoint))
- : node->get_global_transform().affine_inverse().xform( snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) );
+ Vector2 cpoint = !mm.mod.alt? canvas_item_editor->snap_point(xform.affine_inverse().xform(gpoint))
+ : node->get_global_transform().affine_inverse().xform( canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) );
Ref<Curve2D> curve = node->get_curve();
- Vector2 new_pos = moving_from + xform.basis_xform( gpoint - moving_screen_from );
+ Vector2 new_pos = moving_from + xform.affine_inverse().basis_xform(gpoint - moving_screen_from);
switch(action) {
@@ -471,7 +460,7 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
Vector2 gpoint = Point2(mm.x,mm.y);
- edited_point_pos = snap_point(xform.affine_inverse().xform(gpoint));
+ edited_point_pos = canvas_item_editor->snap_point(xform.affine_inverse().xform(gpoint));
canvas_item_editor->update();
}
diff --git a/tools/editor/plugins/path_2d_editor_plugin.h b/tools/editor/plugins/path_2d_editor_plugin.h
index 73de2cc838..9f15c0669f 100644
--- a/tools/editor/plugins/path_2d_editor_plugin.h
+++ b/tools/editor/plugins/path_2d_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -94,7 +94,6 @@ protected:
static void _bind_methods();
public:
- Vector2 snap_point(const Vector2& p_point) const;
bool forward_input_event(const InputEvent& p_event);
void edit(Node *p_path2d);
Path2DEditor(EditorNode *p_editor);
diff --git a/tools/editor/plugins/path_editor_plugin.cpp b/tools/editor/plugins/path_editor_plugin.cpp
index 3f540a3bf4..f4bdf50fe9 100644
--- a/tools/editor/plugins/path_editor_plugin.cpp
+++ b/tools/editor/plugins/path_editor_plugin.cpp
@@ -1,597 +1,597 @@
-/*************************************************************************/
-/* path_editor_plugin.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* http://www.godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-#include "path_editor_plugin.h"
-#include "spatial_editor_plugin.h"
-#include "scene/resources/curve.h"
-#include "os/keyboard.h"
-
-String PathSpatialGizmo::get_handle_name(int p_idx) const {
-
- Ref<Curve3D> c = path->get_curve();
- if (c.is_null())
- return "";
-
- if (p_idx<c->get_point_count()) {
-
- return "Curve Point #"+itos(p_idx);
- }
-
- p_idx=p_idx-c->get_point_count()+1;
-
- int idx=p_idx/2;
- int t=p_idx%2;
- String n = "Curve Point #"+itos(idx);
- if (t==0)
- n+=" In";
- else
- n+=" Out";
-
- return n;
-
-
-}
-Variant PathSpatialGizmo::get_handle_value(int p_idx) const{
-
- Ref<Curve3D> c = path->get_curve();
- if (c.is_null())
- return Variant();
-
- if (p_idx<c->get_point_count()) {
-
- original=c->get_point_pos(p_idx);
- return original;
- }
-
- p_idx=p_idx-c->get_point_count()+1;
-
- int idx=p_idx/2;
- int t=p_idx%2;
-
- Vector3 ofs;
- if (t==0)
- ofs=c->get_point_in(idx);
- else
- ofs= c->get_point_out(idx);
-
- original=ofs+c->get_point_pos(idx);
-
- return ofs;
-
-}
-void PathSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){
-
- Ref<Curve3D> c = path->get_curve();
- if (c.is_null())
- return;
-
- Transform gt = path->get_global_transform();
- Transform gi = gt.affine_inverse();
- Vector3 ray_from = p_camera->project_ray_origin(p_point);
- Vector3 ray_dir = p_camera->project_ray_normal(p_point);
-
- if (p_idx<c->get_point_count()) {
-
- Plane p(gt.xform(original),p_camera->get_transform().basis.get_axis(2));
-
- Vector3 inters;
-
- if (p.intersects_ray(ray_from,ray_dir,&inters)) {
-
- Vector3 local = gi.xform(inters);
- c->set_point_pos(p_idx,local);
- }
-
- return;
- }
-
- p_idx=p_idx-c->get_point_count()+1;
-
- int idx=p_idx/2;
- int t=p_idx%2;
-
- Vector3 base = c->get_point_pos(idx);
-
- Plane p(gt.xform(original),p_camera->get_transform().basis.get_axis(2));
-
- Vector3 inters;
-
- if (p.intersects_ray(ray_from,ray_dir,&inters)) {
-
- Vector3 local = gi.xform(inters)-base;
- if (t==0) {
- c->set_point_in(idx,local);
- } else {
- c->set_point_out(idx,local);
- }
- }
-
-}
-
-void PathSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
-
- Ref<Curve3D> c = path->get_curve();
- if (c.is_null())
- return;
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
-
- if (p_idx<c->get_point_count()) {
-
- if (p_cancel) {
-
- c->set_point_pos(p_idx,p_restore);
- return;
- }
- ur->create_action("Set Curve Point Pos");
- ur->add_do_method(c.ptr(),"set_point_pos",p_idx,c->get_point_pos(p_idx));
- ur->add_undo_method(c.ptr(),"set_point_pos",p_idx,p_restore);
- ur->commit_action();
-
- return;
- }
-
- p_idx=p_idx-c->get_point_count()+1;
-
- int idx=p_idx/2;
- int t=p_idx%2;
-
- Vector3 ofs;
-
- if (p_cancel) {
-
-
-
- return;
- }
-
-
-
- if (t==0) {
-
- if (p_cancel) {
-
- c->set_point_in(p_idx,p_restore);
- return;
- }
- ur->create_action("Set Curve In Pos");
- ur->add_do_method(c.ptr(),"set_point_in",idx,c->get_point_in(idx));
- ur->add_undo_method(c.ptr(),"set_point_in",idx,p_restore);
- ur->commit_action();
-
-
- } else {
- if (p_cancel) {
-
- c->set_point_out(idx,p_restore);
- return;
- }
- ur->create_action("Set Curve Out Pos");
- ur->add_do_method(c.ptr(),"set_point_out",idx,c->get_point_out(idx));
- ur->add_undo_method(c.ptr(),"set_point_out",idx,p_restore);
- ur->commit_action();
-
- }
-
-}
-
-
-void PathSpatialGizmo::redraw(){
-
- clear();
-
- Ref<Curve3D> c = path->get_curve();
- if (c.is_null())
- return;
-
- Vector3Array v3a=c->tesselate();
- //Vector3Array v3a=c->get_baked_points();
-
- int v3s = v3a.size();
- if (v3s==0)
- return;
- Vector<Vector3> v3p;
- Vector3Array::Read r = v3a.read();
-
- for(int i=0;i<v3s-1;i++) {
-
- v3p.push_back(r[i]);
- v3p.push_back(r[i+1]);
- //v3p.push_back(r[i]);
- //v3p.push_back(r[i]+Vector3(0,0.2,0));
- }
-
- add_lines(v3p,PathEditorPlugin::singleton->path_material);
- add_collision_segments(v3p);
-
- if (PathEditorPlugin::singleton->get_edited_path()==path) {
- v3p.clear();
- Vector<Vector3> handles;
- Vector<Vector3> sec_handles;
-
- for(int i=0;i<c->get_point_count();i++) {
-
- Vector3 p = c->get_point_pos(i);
- handles.push_back(p);
- if (i>0) {
- v3p.push_back(p);
- v3p.push_back(p+c->get_point_in(i));
- sec_handles.push_back(p+c->get_point_in(i));
- }
-
- if (i<c->get_point_count()-1) {
- v3p.push_back(p);
- v3p.push_back(p+c->get_point_out(i));
- sec_handles.push_back(p+c->get_point_out(i));
- }
- }
-
- add_lines(v3p,PathEditorPlugin::singleton->path_thin_material);
- add_handles(handles);
- add_handles(sec_handles,false,true);
- }
-
-}
-
-PathSpatialGizmo::PathSpatialGizmo(Path* p_path){
-
- path=p_path;
- set_spatial_node(p_path);
-
-
-
-}
-
-bool PathEditorPlugin::create_spatial_gizmo(Spatial* p_spatial) {
-
- if (p_spatial->cast_to<Path>()) {
-
-
- Ref<PathSpatialGizmo> psg = memnew( PathSpatialGizmo(p_spatial->cast_to<Path>()));
- p_spatial->set_gizmo(psg);
- return true;
- }
-
- return false;
-}
-
-bool PathEditorPlugin::forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) {
-
- if (!path)
- return false;
- Ref<Curve3D> c=path->get_curve();
- if (c.is_null())
- return false;
- Transform gt = path->get_global_transform();
- Transform it = gt.affine_inverse();
-
- static const int click_dist = 10; //should make global
-
-
- if (p_event.type==InputEvent::MOUSE_BUTTON) {
-
- const InputEventMouseButton &mb=p_event.mouse_button;
- Point2 mbpos(mb.x,mb.y);
-
- if (mb.pressed && mb.button_index==BUTTON_LEFT && (curve_create->is_pressed() || (curve_edit->is_pressed() && mb.mod.control))) {
- //click into curve, break it down
- Vector3Array v3a = c->tesselate();
- int idx=0;
- int rc=v3a.size();
- int closest_seg=-1;
- Vector3 closest_seg_point;
- float closest_d=1e20;
-
- if (rc>=2) {
- Vector3Array::Read r = v3a.read();
-
- if (p_camera->unproject_position(gt.xform(c->get_point_pos(0))).distance_to(mbpos)<click_dist)
- return false; //nope, existing
-
-
- for(int i=0;i<c->get_point_count()-1;i++) {
- //find the offset and point index of the place to break up
- int j=idx;
- if (p_camera->unproject_position(gt.xform(c->get_point_pos(i+1))).distance_to(mbpos)<click_dist)
- return false; //nope, existing
-
-
- while(j<rc && c->get_point_pos(i+1)!=r[j]) {
-
- Vector3 from =r[j];
- Vector3 to =r[j+1];
- real_t cdist = from.distance_to(to);
- from=gt.xform(from);
- to=gt.xform(to);
- if (cdist>0) {
- Vector2 s[2];
- s[0] = p_camera->unproject_position(from);
- s[1] = p_camera->unproject_position(to);
- Vector2 inters = Geometry::get_closest_point_to_segment_2d(mbpos,s);
- float d = inters.distance_to(mbpos);
-
- if (d<10 && d<closest_d) {
-
-
- closest_d=d;
- closest_seg=i;
- Vector3 ray_from=p_camera->project_ray_origin(mbpos);
- Vector3 ray_dir=p_camera->project_ray_normal(mbpos);
-
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(ray_from,ray_from+ray_dir*4096,from,to,ra,rb);
-
- closest_seg_point=it.xform(rb);
- }
-
- }
- j++;
-
- }
- if (idx==j)
- idx++; //force next
- else
- idx=j; //swap
-
-
- if (j==rc)
- break;
- }
- }
-
- UndoRedo *ur = editor->get_undo_redo();
- if (closest_seg!=-1) {
- //subdivide
-
- ur->create_action("Split Path");
- ur->add_do_method(c.ptr(),"add_point",closest_seg_point,Vector3(),Vector3(),closest_seg+1);
- ur->add_undo_method(c.ptr(),"remove_point",closest_seg+1);
- ur->commit_action();;
- return true;
-
- } else {
-
- Vector3 org;
- if (c->get_point_count()==0)
- org=path->get_transform().get_origin();
- else
- org=gt.xform(c->get_point_pos(c->get_point_count()));
- Plane p(org,p_camera->get_transform().basis.get_axis(2));
- Vector3 ray_from=p_camera->project_ray_origin(mbpos);
- Vector3 ray_dir=p_camera->project_ray_normal(mbpos);
-
- Vector3 inters;
- if (p.intersects_ray(ray_from,ray_dir,&inters)) {
-
- ur->create_action("Add Point to Curve");
- ur->add_do_method(c.ptr(),"add_point",it.xform(inters),Vector3(),Vector3(),-1);
- ur->add_undo_method(c.ptr(),"remove_point",c->get_point_count());
- ur->commit_action();;
- return true;
- }
-
- //add new at pos
- }
-
- } else if (mb.pressed && ((mb.button_index==BUTTON_LEFT && curve_del->is_pressed()) || (mb.button_index==BUTTON_RIGHT && curve_edit->is_pressed()))) {
-
- int erase_idx=-1;
- for(int i=0;i<c->get_point_count();i++) {
- //find the offset and point index of the place to break up
- if (p_camera->unproject_position(gt.xform(c->get_point_pos(i))).distance_to(mbpos)<click_dist) {
-
- erase_idx=i;
- break;
- }
- }
-
- if (erase_idx!=-1) {
-
- UndoRedo *ur = editor->get_undo_redo();
- ur->create_action("Remove Path Point");
- ur->add_do_method(c.ptr(),"remove_point",erase_idx);
- ur->add_undo_method(c.ptr(),"add_point",c->get_point_pos(erase_idx),c->get_point_in(erase_idx),c->get_point_out(erase_idx),erase_idx);
- ur->commit_action();
- return true;
- }
- }
-
- }
-
- return false;
-}
-
-
-void PathEditorPlugin::edit(Object *p_object) {
-
- if (p_object) {
- path=p_object->cast_to<Path>();
- if (path) {
-
- if (path->get_curve().is_valid()) {
- path->get_curve()->emit_signal("changed");
- }
- }
- } else {
- Path *pre=path;
- path=NULL;
- if (pre) {
- pre->get_curve()->emit_signal("changed");
- }
- }
-// collision_polygon_editor->edit(p_object->cast_to<Node>());
-}
-
-bool PathEditorPlugin::handles(Object *p_object) const {
-
- return p_object->is_type("Path");
-}
-
-void PathEditorPlugin::make_visible(bool p_visible) {
-
- if (p_visible) {
-
- curve_create->show();
- curve_edit->show();
- curve_del->show();
- curve_close->show();
- sep->show();
- } else {
-
- curve_create->hide();
- curve_edit->hide();
- curve_del->hide();
- curve_close->hide();
- sep->hide();
-
- {
- Path *pre=path;
- path=NULL;
- if (pre && pre->get_curve().is_valid()) {
- pre->get_curve()->emit_signal("changed");
- }
- }
- }
-
-}
-
-void PathEditorPlugin::_mode_changed(int p_idx) {
-
- curve_create->set_pressed(p_idx==0);
- curve_edit->set_pressed(p_idx==1);
- curve_del->set_pressed(p_idx==2);
-}
-
-void PathEditorPlugin::_close_curve() {
-
- Ref<Curve3D> c = path->get_curve();
- if (c.is_null())
- return ;
- if (c->get_point_count()<2)
- return;
- c->add_point(c->get_point_pos(0),c->get_point_in(0),c->get_point_out(0));
-
-}
-
-void PathEditorPlugin::_notification(int p_what) {
-
- if (p_what==NOTIFICATION_ENTER_TREE) {
-
- curve_create->connect("pressed",this,"_mode_changed",make_binds(0));
- curve_edit->connect("pressed",this,"_mode_changed",make_binds(1));
- curve_del->connect("pressed",this,"_mode_changed",make_binds(2));
- curve_close->connect("pressed",this,"_close_curve");
- }
-}
-
-void PathEditorPlugin::_bind_methods() {
-
- ObjectTypeDB::bind_method(_MD("_mode_changed"),&PathEditorPlugin::_mode_changed);
- ObjectTypeDB::bind_method(_MD("_close_curve"),&PathEditorPlugin::_close_curve);
-}
-
-PathEditorPlugin* PathEditorPlugin::singleton=NULL;
-
-
-PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) {
-
- path=NULL;
- editor=p_node;
- singleton=this;
-
- path_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
- path_material->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.5,0.5,1.0,0.8) );
- path_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- path_material->set_line_width(3);
- path_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
- path_material->set_flag(Material::FLAG_UNSHADED,true);
-
- path_thin_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
- path_thin_material->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.5,0.5,1.0,0.4) );
- path_thin_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- path_thin_material->set_line_width(1);
- path_thin_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
- path_thin_material->set_flag(Material::FLAG_UNSHADED,true);
-
- SpatialEditor::get_singleton()->add_gizmo_plugin(this);
-
- sep = memnew( VSeparator);
- sep->hide();
- SpatialEditor::get_singleton()->add_control_to_menu_panel(sep);
- curve_edit = memnew( ToolButton );
- curve_edit->set_icon(SpatialEditor::get_singleton()->get_icon("CurveEdit","EditorIcons"));
- curve_edit->set_toggle_mode(true);
- curve_edit->hide();
- curve_edit->set_focus_mode(Control::FOCUS_NONE);
- curve_edit->set_tooltip("Select Points\nShift+Drag: Select Control Points\n"+keycode_get_string(KEY_MASK_CMD)+"Click: Add Point\nRight Click: Delete Point.");
- SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_edit);
- curve_create = memnew( ToolButton );
- curve_create->set_icon(SpatialEditor::get_singleton()->get_icon("CurveCreate","EditorIcons"));
- curve_create->set_toggle_mode(true);
- curve_create->hide();
- curve_create->set_focus_mode(Control::FOCUS_NONE);
- curve_create->set_tooltip("Add Point (in empty space)\nSplit Segment (in curve).");
- SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_create);
- curve_del = memnew( ToolButton );
- curve_del->set_icon(SpatialEditor::get_singleton()->get_icon("CurveDelete","EditorIcons"));
- curve_del->set_toggle_mode(true);
- curve_del->hide();
- curve_del->set_focus_mode(Control::FOCUS_NONE);
- curve_del->set_tooltip("Delete Point.");
- SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_del);
- curve_close = memnew( ToolButton );
- curve_close->set_icon(SpatialEditor::get_singleton()->get_icon("CurveClose","EditorIcons"));
- curve_close->hide();
- curve_close->set_focus_mode(Control::FOCUS_NONE);
- curve_close->set_tooltip("Close Curve");
- SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_close);
-
-
-
- curve_edit->set_pressed(true);
- /*
- collision_polygon_editor = memnew( PathEditor(p_node) );
- editor->get_viewport()->add_child(collision_polygon_editor);
-
- collision_polygon_editor->set_margin(MARGIN_LEFT,200);
- collision_polygon_editor->set_margin(MARGIN_RIGHT,230);
- collision_polygon_editor->set_margin(MARGIN_TOP,0);
- collision_polygon_editor->set_margin(MARGIN_BOTTOM,10);
-
-
- collision_polygon_editor->hide();
- */
-
-
-}
-
-
-PathEditorPlugin::~PathEditorPlugin()
-{
-}
-
+/*************************************************************************/
+/* path_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "path_editor_plugin.h"
+#include "spatial_editor_plugin.h"
+#include "scene/resources/curve.h"
+#include "os/keyboard.h"
+
+String PathSpatialGizmo::get_handle_name(int p_idx) const {
+
+ Ref<Curve3D> c = path->get_curve();
+ if (c.is_null())
+ return "";
+
+ if (p_idx<c->get_point_count()) {
+
+ return "Curve Point #"+itos(p_idx);
+ }
+
+ p_idx=p_idx-c->get_point_count()+1;
+
+ int idx=p_idx/2;
+ int t=p_idx%2;
+ String n = "Curve Point #"+itos(idx);
+ if (t==0)
+ n+=" In";
+ else
+ n+=" Out";
+
+ return n;
+
+
+}
+Variant PathSpatialGizmo::get_handle_value(int p_idx) const{
+
+ Ref<Curve3D> c = path->get_curve();
+ if (c.is_null())
+ return Variant();
+
+ if (p_idx<c->get_point_count()) {
+
+ original=c->get_point_pos(p_idx);
+ return original;
+ }
+
+ p_idx=p_idx-c->get_point_count()+1;
+
+ int idx=p_idx/2;
+ int t=p_idx%2;
+
+ Vector3 ofs;
+ if (t==0)
+ ofs=c->get_point_in(idx);
+ else
+ ofs= c->get_point_out(idx);
+
+ original=ofs+c->get_point_pos(idx);
+
+ return ofs;
+
+}
+void PathSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){
+
+ Ref<Curve3D> c = path->get_curve();
+ if (c.is_null())
+ return;
+
+ Transform gt = path->get_global_transform();
+ Transform gi = gt.affine_inverse();
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ if (p_idx<c->get_point_count()) {
+
+ Plane p(gt.xform(original),p_camera->get_transform().basis.get_axis(2));
+
+ Vector3 inters;
+
+ if (p.intersects_ray(ray_from,ray_dir,&inters)) {
+
+ Vector3 local = gi.xform(inters);
+ c->set_point_pos(p_idx,local);
+ }
+
+ return;
+ }
+
+ p_idx=p_idx-c->get_point_count()+1;
+
+ int idx=p_idx/2;
+ int t=p_idx%2;
+
+ Vector3 base = c->get_point_pos(idx);
+
+ Plane p(gt.xform(original),p_camera->get_transform().basis.get_axis(2));
+
+ Vector3 inters;
+
+ if (p.intersects_ray(ray_from,ray_dir,&inters)) {
+
+ Vector3 local = gi.xform(inters)-base;
+ if (t==0) {
+ c->set_point_in(idx,local);
+ } else {
+ c->set_point_out(idx,local);
+ }
+ }
+
+}
+
+void PathSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
+
+ Ref<Curve3D> c = path->get_curve();
+ if (c.is_null())
+ return;
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+
+ if (p_idx<c->get_point_count()) {
+
+ if (p_cancel) {
+
+ c->set_point_pos(p_idx,p_restore);
+ return;
+ }
+ ur->create_action("Set Curve Point Pos");
+ ur->add_do_method(c.ptr(),"set_point_pos",p_idx,c->get_point_pos(p_idx));
+ ur->add_undo_method(c.ptr(),"set_point_pos",p_idx,p_restore);
+ ur->commit_action();
+
+ return;
+ }
+
+ p_idx=p_idx-c->get_point_count()+1;
+
+ int idx=p_idx/2;
+ int t=p_idx%2;
+
+ Vector3 ofs;
+
+ if (p_cancel) {
+
+
+
+ return;
+ }
+
+
+
+ if (t==0) {
+
+ if (p_cancel) {
+
+ c->set_point_in(p_idx,p_restore);
+ return;
+ }
+ ur->create_action("Set Curve In Pos");
+ ur->add_do_method(c.ptr(),"set_point_in",idx,c->get_point_in(idx));
+ ur->add_undo_method(c.ptr(),"set_point_in",idx,p_restore);
+ ur->commit_action();
+
+
+ } else {
+ if (p_cancel) {
+
+ c->set_point_out(idx,p_restore);
+ return;
+ }
+ ur->create_action("Set Curve Out Pos");
+ ur->add_do_method(c.ptr(),"set_point_out",idx,c->get_point_out(idx));
+ ur->add_undo_method(c.ptr(),"set_point_out",idx,p_restore);
+ ur->commit_action();
+
+ }
+
+}
+
+
+void PathSpatialGizmo::redraw(){
+
+ clear();
+
+ Ref<Curve3D> c = path->get_curve();
+ if (c.is_null())
+ return;
+
+ Vector3Array v3a=c->tesselate();
+ //Vector3Array v3a=c->get_baked_points();
+
+ int v3s = v3a.size();
+ if (v3s==0)
+ return;
+ Vector<Vector3> v3p;
+ Vector3Array::Read r = v3a.read();
+
+ for(int i=0;i<v3s-1;i++) {
+
+ v3p.push_back(r[i]);
+ v3p.push_back(r[i+1]);
+ //v3p.push_back(r[i]);
+ //v3p.push_back(r[i]+Vector3(0,0.2,0));
+ }
+
+ add_lines(v3p,PathEditorPlugin::singleton->path_material);
+ add_collision_segments(v3p);
+
+ if (PathEditorPlugin::singleton->get_edited_path()==path) {
+ v3p.clear();
+ Vector<Vector3> handles;
+ Vector<Vector3> sec_handles;
+
+ for(int i=0;i<c->get_point_count();i++) {
+
+ Vector3 p = c->get_point_pos(i);
+ handles.push_back(p);
+ if (i>0) {
+ v3p.push_back(p);
+ v3p.push_back(p+c->get_point_in(i));
+ sec_handles.push_back(p+c->get_point_in(i));
+ }
+
+ if (i<c->get_point_count()-1) {
+ v3p.push_back(p);
+ v3p.push_back(p+c->get_point_out(i));
+ sec_handles.push_back(p+c->get_point_out(i));
+ }
+ }
+
+ add_lines(v3p,PathEditorPlugin::singleton->path_thin_material);
+ add_handles(handles);
+ add_handles(sec_handles,false,true);
+ }
+
+}
+
+PathSpatialGizmo::PathSpatialGizmo(Path* p_path){
+
+ path=p_path;
+ set_spatial_node(p_path);
+
+
+
+}
+
+bool PathEditorPlugin::create_spatial_gizmo(Spatial* p_spatial) {
+
+ if (p_spatial->cast_to<Path>()) {
+
+
+ Ref<PathSpatialGizmo> psg = memnew( PathSpatialGizmo(p_spatial->cast_to<Path>()));
+ p_spatial->set_gizmo(psg);
+ return true;
+ }
+
+ return false;
+}
+
+bool PathEditorPlugin::forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) {
+
+ if (!path)
+ return false;
+ Ref<Curve3D> c=path->get_curve();
+ if (c.is_null())
+ return false;
+ Transform gt = path->get_global_transform();
+ Transform it = gt.affine_inverse();
+
+ static const int click_dist = 10; //should make global
+
+
+ if (p_event.type==InputEvent::MOUSE_BUTTON) {
+
+ const InputEventMouseButton &mb=p_event.mouse_button;
+ Point2 mbpos(mb.x,mb.y);
+
+ if (mb.pressed && mb.button_index==BUTTON_LEFT && (curve_create->is_pressed() || (curve_edit->is_pressed() && mb.mod.control))) {
+ //click into curve, break it down
+ Vector3Array v3a = c->tesselate();
+ int idx=0;
+ int rc=v3a.size();
+ int closest_seg=-1;
+ Vector3 closest_seg_point;
+ float closest_d=1e20;
+
+ if (rc>=2) {
+ Vector3Array::Read r = v3a.read();
+
+ if (p_camera->unproject_position(gt.xform(c->get_point_pos(0))).distance_to(mbpos)<click_dist)
+ return false; //nope, existing
+
+
+ for(int i=0;i<c->get_point_count()-1;i++) {
+ //find the offset and point index of the place to break up
+ int j=idx;
+ if (p_camera->unproject_position(gt.xform(c->get_point_pos(i+1))).distance_to(mbpos)<click_dist)
+ return false; //nope, existing
+
+
+ while(j<rc && c->get_point_pos(i+1)!=r[j]) {
+
+ Vector3 from =r[j];
+ Vector3 to =r[j+1];
+ real_t cdist = from.distance_to(to);
+ from=gt.xform(from);
+ to=gt.xform(to);
+ if (cdist>0) {
+ Vector2 s[2];
+ s[0] = p_camera->unproject_position(from);
+ s[1] = p_camera->unproject_position(to);
+ Vector2 inters = Geometry::get_closest_point_to_segment_2d(mbpos,s);
+ float d = inters.distance_to(mbpos);
+
+ if (d<10 && d<closest_d) {
+
+
+ closest_d=d;
+ closest_seg=i;
+ Vector3 ray_from=p_camera->project_ray_origin(mbpos);
+ Vector3 ray_dir=p_camera->project_ray_normal(mbpos);
+
+ Vector3 ra,rb;
+ Geometry::get_closest_points_between_segments(ray_from,ray_from+ray_dir*4096,from,to,ra,rb);
+
+ closest_seg_point=it.xform(rb);
+ }
+
+ }
+ j++;
+
+ }
+ if (idx==j)
+ idx++; //force next
+ else
+ idx=j; //swap
+
+
+ if (j==rc)
+ break;
+ }
+ }
+
+ UndoRedo *ur = editor->get_undo_redo();
+ if (closest_seg!=-1) {
+ //subdivide
+
+ ur->create_action("Split Path");
+ ur->add_do_method(c.ptr(),"add_point",closest_seg_point,Vector3(),Vector3(),closest_seg+1);
+ ur->add_undo_method(c.ptr(),"remove_point",closest_seg+1);
+ ur->commit_action();;
+ return true;
+
+ } else {
+
+ Vector3 org;
+ if (c->get_point_count()==0)
+ org=path->get_transform().get_origin();
+ else
+ org=gt.xform(c->get_point_pos(c->get_point_count()));
+ Plane p(org,p_camera->get_transform().basis.get_axis(2));
+ Vector3 ray_from=p_camera->project_ray_origin(mbpos);
+ Vector3 ray_dir=p_camera->project_ray_normal(mbpos);
+
+ Vector3 inters;
+ if (p.intersects_ray(ray_from,ray_dir,&inters)) {
+
+ ur->create_action("Add Point to Curve");
+ ur->add_do_method(c.ptr(),"add_point",it.xform(inters),Vector3(),Vector3(),-1);
+ ur->add_undo_method(c.ptr(),"remove_point",c->get_point_count());
+ ur->commit_action();;
+ return true;
+ }
+
+ //add new at pos
+ }
+
+ } else if (mb.pressed && ((mb.button_index==BUTTON_LEFT && curve_del->is_pressed()) || (mb.button_index==BUTTON_RIGHT && curve_edit->is_pressed()))) {
+
+ int erase_idx=-1;
+ for(int i=0;i<c->get_point_count();i++) {
+ //find the offset and point index of the place to break up
+ if (p_camera->unproject_position(gt.xform(c->get_point_pos(i))).distance_to(mbpos)<click_dist) {
+
+ erase_idx=i;
+ break;
+ }
+ }
+
+ if (erase_idx!=-1) {
+
+ UndoRedo *ur = editor->get_undo_redo();
+ ur->create_action("Remove Path Point");
+ ur->add_do_method(c.ptr(),"remove_point",erase_idx);
+ ur->add_undo_method(c.ptr(),"add_point",c->get_point_pos(erase_idx),c->get_point_in(erase_idx),c->get_point_out(erase_idx),erase_idx);
+ ur->commit_action();
+ return true;
+ }
+ }
+
+ }
+
+ return false;
+}
+
+
+void PathEditorPlugin::edit(Object *p_object) {
+
+ if (p_object) {
+ path=p_object->cast_to<Path>();
+ if (path) {
+
+ if (path->get_curve().is_valid()) {
+ path->get_curve()->emit_signal("changed");
+ }
+ }
+ } else {
+ Path *pre=path;
+ path=NULL;
+ if (pre) {
+ pre->get_curve()->emit_signal("changed");
+ }
+ }
+// collision_polygon_editor->edit(p_object->cast_to<Node>());
+}
+
+bool PathEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("Path");
+}
+
+void PathEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+
+ curve_create->show();
+ curve_edit->show();
+ curve_del->show();
+ curve_close->show();
+ sep->show();
+ } else {
+
+ curve_create->hide();
+ curve_edit->hide();
+ curve_del->hide();
+ curve_close->hide();
+ sep->hide();
+
+ {
+ Path *pre=path;
+ path=NULL;
+ if (pre && pre->get_curve().is_valid()) {
+ pre->get_curve()->emit_signal("changed");
+ }
+ }
+ }
+
+}
+
+void PathEditorPlugin::_mode_changed(int p_idx) {
+
+ curve_create->set_pressed(p_idx==0);
+ curve_edit->set_pressed(p_idx==1);
+ curve_del->set_pressed(p_idx==2);
+}
+
+void PathEditorPlugin::_close_curve() {
+
+ Ref<Curve3D> c = path->get_curve();
+ if (c.is_null())
+ return ;
+ if (c->get_point_count()<2)
+ return;
+ c->add_point(c->get_point_pos(0),c->get_point_in(0),c->get_point_out(0));
+
+}
+
+void PathEditorPlugin::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+
+ curve_create->connect("pressed",this,"_mode_changed",make_binds(0));
+ curve_edit->connect("pressed",this,"_mode_changed",make_binds(1));
+ curve_del->connect("pressed",this,"_mode_changed",make_binds(2));
+ curve_close->connect("pressed",this,"_close_curve");
+ }
+}
+
+void PathEditorPlugin::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_mode_changed"),&PathEditorPlugin::_mode_changed);
+ ObjectTypeDB::bind_method(_MD("_close_curve"),&PathEditorPlugin::_close_curve);
+}
+
+PathEditorPlugin* PathEditorPlugin::singleton=NULL;
+
+
+PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) {
+
+ path=NULL;
+ editor=p_node;
+ singleton=this;
+
+ path_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ path_material->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.5,0.5,1.0,0.8) );
+ path_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ path_material->set_line_width(3);
+ path_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ path_material->set_flag(Material::FLAG_UNSHADED,true);
+
+ path_thin_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ path_thin_material->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.5,0.5,1.0,0.4) );
+ path_thin_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ path_thin_material->set_line_width(1);
+ path_thin_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ path_thin_material->set_flag(Material::FLAG_UNSHADED,true);
+
+ SpatialEditor::get_singleton()->add_gizmo_plugin(this);
+
+ sep = memnew( VSeparator);
+ sep->hide();
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(sep);
+ curve_edit = memnew( ToolButton );
+ curve_edit->set_icon(SpatialEditor::get_singleton()->get_icon("CurveEdit","EditorIcons"));
+ curve_edit->set_toggle_mode(true);
+ curve_edit->hide();
+ curve_edit->set_focus_mode(Control::FOCUS_NONE);
+ curve_edit->set_tooltip("Select Points\nShift+Drag: Select Control Points\n"+keycode_get_string(KEY_MASK_CMD)+"Click: Add Point\nRight Click: Delete Point.");
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_edit);
+ curve_create = memnew( ToolButton );
+ curve_create->set_icon(SpatialEditor::get_singleton()->get_icon("CurveCreate","EditorIcons"));
+ curve_create->set_toggle_mode(true);
+ curve_create->hide();
+ curve_create->set_focus_mode(Control::FOCUS_NONE);
+ curve_create->set_tooltip("Add Point (in empty space)\nSplit Segment (in curve).");
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_create);
+ curve_del = memnew( ToolButton );
+ curve_del->set_icon(SpatialEditor::get_singleton()->get_icon("CurveDelete","EditorIcons"));
+ curve_del->set_toggle_mode(true);
+ curve_del->hide();
+ curve_del->set_focus_mode(Control::FOCUS_NONE);
+ curve_del->set_tooltip("Delete Point.");
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_del);
+ curve_close = memnew( ToolButton );
+ curve_close->set_icon(SpatialEditor::get_singleton()->get_icon("CurveClose","EditorIcons"));
+ curve_close->hide();
+ curve_close->set_focus_mode(Control::FOCUS_NONE);
+ curve_close->set_tooltip("Close Curve");
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_close);
+
+
+
+ curve_edit->set_pressed(true);
+ /*
+ collision_polygon_editor = memnew( PathEditor(p_node) );
+ editor->get_viewport()->add_child(collision_polygon_editor);
+
+ collision_polygon_editor->set_margin(MARGIN_LEFT,200);
+ collision_polygon_editor->set_margin(MARGIN_RIGHT,230);
+ collision_polygon_editor->set_margin(MARGIN_TOP,0);
+ collision_polygon_editor->set_margin(MARGIN_BOTTOM,10);
+
+
+ collision_polygon_editor->hide();
+ */
+
+
+}
+
+
+PathEditorPlugin::~PathEditorPlugin()
+{
+}
+
diff --git a/tools/editor/plugins/path_editor_plugin.h b/tools/editor/plugins/path_editor_plugin.h
index d730d33551..fcd4241e59 100644
--- a/tools/editor/plugins/path_editor_plugin.h
+++ b/tools/editor/plugins/path_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/plugins/polygon_2d_editor_plugin.cpp b/tools/editor/plugins/polygon_2d_editor_plugin.cpp
index 27e539d50b..cd82297365 100644
--- a/tools/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/tools/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -50,6 +50,11 @@ void Polygon2DEditor::_notification(int p_what) {
uv_button[UV_MODE_ROTATE]->set_icon(get_icon("ToolRotate","EditorIcons"));
uv_button[UV_MODE_SCALE]->set_icon(get_icon("ToolScale","EditorIcons"));
+ b_snap_grid->set_icon( get_icon("Grid", "EditorIcons"));
+ b_snap_enable->set_icon( get_icon("Snap", "EditorIcons"));
+ uv_icon_zoom->set_texture( get_icon("Zoom", "EditorIcons"));
+
+ get_tree()->connect("node_removed", this, "_node_removed");
} break;
case NOTIFICATION_FIXED_PROCESS: {
@@ -62,24 +67,15 @@ void Polygon2DEditor::_notification(int p_what) {
void Polygon2DEditor::_node_removed(Node *p_node) {
if(p_node==node) {
- node=NULL;
+ edit(NULL);
hide();
+
+ canvas_item_editor->get_viewport_control()->update();
}
}
-Vector2 Polygon2DEditor::snap_point(const Vector2& p_point) const {
-
- if (canvas_item_editor->is_snap_active()) {
-
- return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap());
-
- } else {
- return p_point;
- }
-}
-
void Polygon2DEditor::_menu_option(int p_option) {
switch(p_option) {
@@ -101,7 +97,7 @@ void Polygon2DEditor::_menu_option(int p_option) {
if (node->get_texture().is_null()) {
error->set_text("No texture in this polygon.\nSet a texture to be able to edit UV.");
- error->popup_centered_minsize(Size2(300,70));
+ error->popup_centered_minsize();
return;
}
@@ -169,6 +165,41 @@ void Polygon2DEditor::_menu_option(int p_option) {
}
}
+void Polygon2DEditor::_set_use_snap(bool p_use)
+{
+ use_snap=p_use;
+}
+
+void Polygon2DEditor::_set_show_grid(bool p_show)
+{
+ snap_show_grid=p_show;
+ uv_edit_draw->update();
+}
+
+void Polygon2DEditor::_set_snap_off_x(float p_val)
+{
+ snap_offset.x=p_val;
+ uv_edit_draw->update();
+}
+
+void Polygon2DEditor::_set_snap_off_y(float p_val)
+{
+ snap_offset.y=p_val;
+ uv_edit_draw->update();
+}
+
+void Polygon2DEditor::_set_snap_step_x(float p_val)
+{
+ snap_step.x=p_val;
+ uv_edit_draw->update();
+}
+
+void Polygon2DEditor::_set_snap_step_y(float p_val)
+{
+ snap_step.y=p_val;
+ uv_edit_draw->update();
+}
+
void Polygon2DEditor::_wip_close() {
undo_redo->create_action("Create Poly");
@@ -201,7 +232,7 @@ bool Polygon2DEditor::forward_input_event(const InputEvent& p_event) {
Vector2 gpoint = Point2(mb.x,mb.y);
Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
- cpoint=snap_point(cpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
@@ -405,7 +436,7 @@ bool Polygon2DEditor::forward_input_event(const InputEvent& p_event) {
Vector2 gpoint = Point2(mm.x,mm.y);
Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
- cpoint=snap_point(cpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint);
canvas_item_editor->get_viewport_control()->update();
@@ -505,7 +536,7 @@ void Polygon2DEditor::_uv_input(const InputEvent& p_input) {
Vector2 tuv=mtx.xform(uv_prev[i]);
if (tuv.distance_to(Vector2(mb.x,mb.y))<8) {
-
+ uv_drag_from=tuv;
uv_drag_index=i;
}
}
@@ -556,7 +587,7 @@ void Polygon2DEditor::_uv_input(const InputEvent& p_input) {
} else if (uv_drag) {
- Vector2 uv_drag_to(mm.x,mm.y);
+ Vector2 uv_drag_to=snap_point(Vector2(mm.x,mm.y));
Vector2 drag = mtx.affine_inverse().xform(uv_drag_to) - mtx.affine_inverse().xform(uv_drag_from);
@@ -660,6 +691,33 @@ void Polygon2DEditor::_uv_draw() {
uv_edit_draw->draw_texture(base_tex,Point2());
VS::get_singleton()->canvas_item_add_set_transform(uv_edit_draw->get_canvas_item(),Matrix32());
+ if (snap_show_grid) {
+ Size2 s = uv_edit_draw->get_size();
+ int last_cell;
+
+ if (snap_step.x!=0) {
+ for(int i=0;i<s.width;i++) {
+ int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(i,0)).x-snap_offset.x)/snap_step.x));
+ if (i==0)
+ last_cell=cell;
+ if (last_cell!=cell)
+ uv_edit_draw->draw_line(Point2(i,0),Point2(i,s.height),Color(0.3,0.7,1,0.3));
+ last_cell=cell;
+ }
+ }
+
+ if (snap_step.y!=0) {
+ for(int i=0;i<s.height;i++) {
+ int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(0,i)).y-snap_offset.y)/snap_step.y));
+ if (i==0)
+ last_cell=cell;
+ if (last_cell!=cell)
+ uv_edit_draw->draw_line(Point2(0,i),Point2(s.width,i),Color(0.3,0.7,1,0.3));
+ last_cell=cell;
+ }
+ }
+ }
+
DVector<Vector2> uvs = node->get_uv();
Ref<Texture> handle = get_icon("EditorHandle","EditorIcons");
@@ -703,16 +761,13 @@ void Polygon2DEditor::edit(Node *p_collision_polygon) {
node=p_collision_polygon->cast_to<Polygon2D>();
if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw");
- node->connect("exit_tree",this,"_node_removed",varray(),CONNECT_ONESHOT);
+
wip.clear();
wip_active=false;
edited_point=-1;
} else {
- if (node)
- node->disconnect("exit_tree",this,"_node_removed");
-
node=NULL;
if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
@@ -731,8 +786,27 @@ void Polygon2DEditor::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_uv_input"),&Polygon2DEditor::_uv_input);
ObjectTypeDB::bind_method(_MD("_uv_scroll_changed"),&Polygon2DEditor::_uv_scroll_changed);
ObjectTypeDB::bind_method(_MD("_node_removed"),&Polygon2DEditor::_node_removed);
+ ObjectTypeDB::bind_method(_MD("_set_use_snap"),&Polygon2DEditor::_set_use_snap);
+ ObjectTypeDB::bind_method(_MD("_set_show_grid"),&Polygon2DEditor::_set_show_grid);
+ ObjectTypeDB::bind_method(_MD("_set_snap_off_x"),&Polygon2DEditor::_set_snap_off_x);
+ ObjectTypeDB::bind_method(_MD("_set_snap_off_y"),&Polygon2DEditor::_set_snap_off_y);
+ ObjectTypeDB::bind_method(_MD("_set_snap_step_x"),&Polygon2DEditor::_set_snap_step_x);
+ ObjectTypeDB::bind_method(_MD("_set_snap_step_y"),&Polygon2DEditor::_set_snap_step_y);
+
+}
+inline float _snap_scalar(float p_offset, float p_step, float p_target) {
+ return p_step != 0 ? Math::stepify(p_target - p_offset, p_step) + p_offset : p_target;
+}
+
+Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const {
+ if (use_snap) {
+ p_target.x = _snap_scalar(snap_offset.x*uv_draw_zoom-uv_draw_ofs.x, snap_step.x*uv_draw_zoom, p_target.x);
+ p_target.y = _snap_scalar(snap_offset.y*uv_draw_zoom-uv_draw_ofs.y, snap_step.y*uv_draw_zoom, p_target.y);
+ }
+
+ return p_target;
}
Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) {
@@ -742,6 +816,10 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) {
editor=p_editor;
undo_redo = editor->get_undo_redo();
+ snap_step=Vector2(10,10);
+ use_snap=false;
+ snap_show_grid=false;
+
add_child( memnew( VSeparator ));
button_create = memnew( ToolButton );
add_child(button_create);
@@ -811,9 +889,72 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) {
uv_menu->get_popup()->add_separator();
uv_menu->get_popup()->add_item("Clear UV",UVEDIT_UV_CLEAR);
uv_menu->get_popup()->connect("item_pressed",this,"_menu_option");
+
+ uv_mode_hb->add_child( memnew( VSeparator ));
+
+ b_snap_enable = memnew( ToolButton );
+ uv_mode_hb->add_child(b_snap_enable);
+ b_snap_enable->set_text("Snap");
+ b_snap_enable->set_focus_mode(FOCUS_NONE);
+ b_snap_enable->set_toggle_mode(true);
+ b_snap_enable->set_pressed(use_snap);
+ b_snap_enable->set_tooltip("Enable Snap");
+ b_snap_enable->connect("toggled",this,"_set_use_snap");
+
+ b_snap_grid = memnew( ToolButton );
+ uv_mode_hb->add_child(b_snap_grid);
+ b_snap_grid->set_text("Grid");
+ b_snap_grid->set_focus_mode(FOCUS_NONE);
+ b_snap_grid->set_toggle_mode(true);
+ b_snap_grid->set_pressed(snap_show_grid);
+ b_snap_grid->set_tooltip("Show Grid");
+ b_snap_grid->connect("toggled",this,"_set_show_grid");
+
+ uv_mode_hb->add_child( memnew( VSeparator ));
+ uv_mode_hb->add_child( memnew( Label("Grid Offset:") ) );
+
+ SpinBox *sb_off_x = memnew( SpinBox );
+ sb_off_x->set_min(-256);
+ sb_off_x->set_max(256);
+ sb_off_x->set_step(1);
+ sb_off_x->set_val(snap_offset.x);
+ sb_off_x->set_suffix("px");
+ sb_off_x->connect("value_changed", this, "_set_snap_off_x");
+ uv_mode_hb->add_child(sb_off_x);
+
+ SpinBox *sb_off_y = memnew( SpinBox );
+ sb_off_y->set_min(-256);
+ sb_off_y->set_max(256);
+ sb_off_y->set_step(1);
+ sb_off_y->set_val(snap_offset.y);
+ sb_off_y->set_suffix("px");
+ sb_off_y->connect("value_changed", this, "_set_snap_off_y");
+ uv_mode_hb->add_child(sb_off_y);
+
+ uv_mode_hb->add_child( memnew( VSeparator ));
+ uv_mode_hb->add_child( memnew( Label("Grid Step:") ) );
+
+ SpinBox *sb_step_x = memnew( SpinBox );
+ sb_step_x->set_min(-256);
+ sb_step_x->set_max(256);
+ sb_step_x->set_step(1);
+ sb_step_x->set_val(snap_step.x);
+ sb_step_x->set_suffix("px");
+ sb_step_x->connect("value_changed", this, "_set_snap_step_x");
+ uv_mode_hb->add_child(sb_step_x);
+
+ SpinBox *sb_step_y = memnew( SpinBox );
+ sb_step_y->set_min(-256);
+ sb_step_y->set_max(256);
+ sb_step_y->set_step(1);
+ sb_step_y->set_val(snap_step.y);
+ sb_step_y->set_suffix("px");
+ sb_step_y->connect("value_changed", this, "_set_snap_step_y");
+ uv_mode_hb->add_child(sb_step_y);
+
uv_mode_hb->add_child( memnew( VSeparator ));
uv_icon_zoom = memnew( TextureFrame );
- uv_main_hb->add_child( uv_icon_zoom );
+ uv_mode_hb->add_child( uv_icon_zoom );
uv_zoom = memnew( HSlider );
uv_zoom->set_min(0.01);
uv_zoom->set_max(4);
diff --git a/tools/editor/plugins/polygon_2d_editor_plugin.h b/tools/editor/plugins/polygon_2d_editor_plugin.h
index 88d1c20493..0939c44264 100644
--- a/tools/editor/plugins/polygon_2d_editor_plugin.h
+++ b/tools/editor/plugins/polygon_2d_editor_plugin.h
@@ -41,6 +41,8 @@ class Polygon2DEditor : public HBoxContainer {
UVMode uv_mode;
AcceptDialog *uv_edit;
ToolButton *uv_button[4];
+ ToolButton *b_snap_enable;
+ ToolButton *b_snap_grid;
Control *uv_edit_draw;
HSlider *uv_zoom;
SpinBox *uv_zoom_value;
@@ -78,6 +80,11 @@ class Polygon2DEditor : public HBoxContainer {
Vector<Vector2> wip;
bool wip_active;
+ bool use_snap;
+ bool snap_show_grid;
+ Vector2 snap_offset;
+ Vector2 snap_step;
+
void _uv_scroll_changed(float);
void _uv_input(const InputEvent& p_input);
void _uv_draw();
@@ -86,13 +93,22 @@ class Polygon2DEditor : public HBoxContainer {
void _canvas_draw();
void _menu_option(int p_option);
+ void _set_use_snap(bool p_use);
+ void _set_show_grid(bool p_show);
+ void _set_snap_off_x(float p_val);
+ void _set_snap_off_y(float p_val);
+ void _set_snap_step_x(float p_val);
+ void _set_snap_step_y(float p_val);
+
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
static void _bind_methods();
+
+ Vector2 snap_point(Vector2 p_target) const;
+
public:
- Vector2 snap_point(const Vector2& p_point) const;
bool forward_input_event(const InputEvent& p_event);
void edit(Node *p_collision_polygon);
Polygon2DEditor(EditorNode *p_editor);
diff --git a/tools/editor/plugins/resource_preloader_editor_plugin.cpp b/tools/editor/plugins/resource_preloader_editor_plugin.cpp
index d9726cac21..9cd20ac53a 100644
--- a/tools/editor/plugins/resource_preloader_editor_plugin.cpp
+++ b/tools/editor/plugins/resource_preloader_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -72,7 +72,7 @@ void ResourcePreloaderEditor::_file_load_request(const String& p_path) {
dialog->set_title("Error!");
//dialog->get_cancel()->set_text("Close");
dialog->get_ok()->set_text("Close");
- dialog->popup_centered(Size2(300,60));
+ dialog->popup_centered_minsize();
return; ///beh should show an error i guess
}
@@ -102,7 +102,7 @@ void ResourcePreloaderEditor::_load_pressed() {
for(int i=0;i<extensions.size();i++)
file->add_filter("*."+extensions[i]);
- file->set_mode(FileDialog::MODE_OPEN_FILE);
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
file->popup_centered_ratio();
@@ -167,7 +167,7 @@ void ResourcePreloaderEditor::_paste_pressed() {
dialog->set_title("Error!");
//dialog->get_cancel()->set_text("Close");
dialog->get_ok()->set_text("Close");
- dialog->popup_centered(Size2(300,60));
+ dialog->popup_centered_minsize();
return; ///beh should show an error i guess
}
@@ -310,7 +310,7 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() {
paste->set_text("Paste");
hbc->add_child(paste);
- file = memnew( FileDialog );
+ file = memnew( EditorFileDialog );
add_child(file);
diff --git a/tools/editor/plugins/resource_preloader_editor_plugin.h b/tools/editor/plugins/resource_preloader_editor_plugin.h
index e3178bc8ff..88272bc603 100644
--- a/tools/editor/plugins/resource_preloader_editor_plugin.h
+++ b/tools/editor/plugins/resource_preloader_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -49,7 +49,7 @@ class ResourcePreloaderEditor : public PanelContainer {
bool loading_scene;
- FileDialog *file;
+ EditorFileDialog *file;
AcceptDialog *dialog;
diff --git a/tools/editor/plugins/rich_text_editor_plugin.cpp b/tools/editor/plugins/rich_text_editor_plugin.cpp
index 58b3636dcc..a0daad854f 100644
--- a/tools/editor/plugins/rich_text_editor_plugin.cpp
+++ b/tools/editor/plugins/rich_text_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,6 +28,8 @@
/*************************************************************************/
#include "rich_text_editor_plugin.h"
#include "os/file_access.h"
+#include "canvas_item_editor_plugin.h"
+
void RichTextEditor::_notification(int p_what) {
switch(p_what) {
@@ -100,7 +102,8 @@ void RichTextEditor::edit(Node *p_rich_text) {
RichTextEditor::RichTextEditor() {
options = memnew( MenuButton );
- add_child(options);
+ //add_child(options);
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(options);
options->set_area_as_parent_rect();
options->set_text("RichText");
@@ -108,10 +111,10 @@ RichTextEditor::RichTextEditor() {
options->get_popup()->add_item("Clear",CLEAR);
options->get_popup()->connect("item_pressed", this,"_menu_option");
- file_dialog = memnew( FileDialog );
+ file_dialog = memnew( EditorFileDialog );
add_child(file_dialog);
file_dialog->add_filter("*.txt");
- file_dialog->set_mode(FileDialog::MODE_OPEN_FILE);
+ file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
file_dialog->connect("file_selected",this,"_file_selected");
}
@@ -129,10 +132,10 @@ bool RichTextEditorPlugin::handles(Object *p_object) const {
void RichTextEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
- rich_text_editor->show();
+ rich_text_editor->options->show();
} else {
- rich_text_editor->hide();
+ rich_text_editor->options->hide();
rich_text_editor->edit(NULL);
}
@@ -149,10 +152,7 @@ RichTextEditorPlugin::RichTextEditorPlugin(EditorNode *p_node) {
rich_text_editor->set_margin(MARGIN_TOP,0);
rich_text_editor->set_margin(MARGIN_BOTTOM,10);
-
- rich_text_editor->hide();
-
-
+ rich_text_editor->options->hide();
}
diff --git a/tools/editor/plugins/rich_text_editor_plugin.h b/tools/editor/plugins/rich_text_editor_plugin.h
index e51e0653b9..478dc0d308 100644
--- a/tools/editor/plugins/rich_text_editor_plugin.h
+++ b/tools/editor/plugins/rich_text_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -42,6 +42,8 @@ class RichTextEditor : public Control {
OBJ_TYPE(RichTextEditor, Control );
+ friend class RichTextEditorPlugin;
+
enum {
PARSE_BBCODE,
@@ -49,9 +51,9 @@ class RichTextEditor : public Control {
};
Panel *panel;
+ MenuButton *options;
RichTextLabel *node;
- MenuButton *options;
- FileDialog *file_dialog;
+ EditorFileDialog *file_dialog;
void _file_selected(const String& p_path);
void _menu_option(int p_option);
diff --git a/tools/editor/plugins/sample_editor_plugin.cpp b/tools/editor/plugins/sample_editor_plugin.cpp
index 83adeee789..d88f2adc73 100644
--- a/tools/editor/plugins/sample_editor_plugin.cpp
+++ b/tools/editor/plugins/sample_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -98,6 +98,131 @@ void SampleEditor::generate_preview_texture(const Ref<Sample>& p_sample,Ref<Imag
if (p_sample->get_format()==Sample::FORMAT_IMA_ADPCM) {
+ struct IMA_ADPCM_State {
+
+ int16_t step_index;
+ int32_t predictor;
+ /* values at loop point */
+ int16_t loop_step_index;
+ int32_t loop_predictor;
+ int32_t last_nibble;
+ int32_t loop_pos;
+ int32_t window_ofs;
+ const uint8_t *ptr;
+ } ima_adpcm;
+
+ ima_adpcm.step_index=0;
+ ima_adpcm.predictor=0;
+ ima_adpcm.loop_step_index=0;
+ ima_adpcm.loop_predictor=0;
+ ima_adpcm.last_nibble=-1;
+ ima_adpcm.loop_pos=0x7FFFFFFF;
+ ima_adpcm.window_ofs=0;
+ ima_adpcm.ptr=NULL;
+
+
+ for(int i=0;i<w;i++) {
+
+ float max[2]={-1e10,-1e10};
+ float min[2]={1e10,1e10};
+ int from = i*len/w;
+ int to = (i+1)*len/w;
+ if (to>=len)
+ to=len-1;
+
+ for(int j=from;j<to;j++) {
+
+ while(j>ima_adpcm.last_nibble) {
+
+ static const int16_t _ima_adpcm_step_table[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+ };
+
+ static const int8_t _ima_adpcm_index_table[16] = {
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+
+ int16_t nibble,signed_nibble,diff,step;
+
+ ima_adpcm.last_nibble++;
+ const uint8_t *src_ptr=sdata;
+
+ int ofs = ima_adpcm.last_nibble>>1;
+
+ if (stereo)
+ ofs*=2;
+
+ nibble = (ima_adpcm.last_nibble&1)?
+ (src_ptr[ofs]>>4):(src_ptr[ofs]&0xF);
+
+ step=_ima_adpcm_step_table[ima_adpcm.step_index];
+
+ ima_adpcm.step_index += _ima_adpcm_index_table[nibble];
+ if (ima_adpcm.step_index<0)
+ ima_adpcm.step_index=0;
+ if (ima_adpcm.step_index>88)
+ ima_adpcm.step_index=88;
+
+ /*
+ signed_nibble = (nibble&7) * ((nibble&8)?-1:1);
+ diff = (2 * signed_nibble + 1) * step / 4; */
+
+ diff = step >> 3 ;
+ if (nibble & 1)
+ diff += step >> 2 ;
+ if (nibble & 2)
+ diff += step >> 1 ;
+ if (nibble & 4)
+ diff += step ;
+ if (nibble & 8)
+ diff = -diff ;
+
+ ima_adpcm.predictor+=diff;
+ if (ima_adpcm.predictor<-0x8000)
+ ima_adpcm.predictor=-0x8000;
+ else if (ima_adpcm.predictor>0x7FFF)
+ ima_adpcm.predictor=0x7FFF;
+
+
+ /* store loop if there */
+ if (ima_adpcm.last_nibble==ima_adpcm.loop_pos) {
+
+ ima_adpcm.loop_step_index = ima_adpcm.step_index;
+ ima_adpcm.loop_predictor = ima_adpcm.predictor;
+ }
+
+ }
+
+ float v=ima_adpcm.predictor/32767.0;
+ if (v>max[0])
+ max[0]=v;
+ if (v<min[0])
+ min[0]=v;
+ }
+
+ for(int j=0;j<h;j++) {
+ float v = (j/(float)h) * 2.0 - 1.0;
+ uint8_t* imgofs = &imgw[(j*w+i)*3];
+ if (v>min[0] && v<max[0]) {
+ imgofs[0]=255;
+ imgofs[1]=150;
+ imgofs[2]=80;
+ } else {
+ imgofs[0]=0;
+ imgofs[1]=0;
+ imgofs[2]=0;
+ }
+ }
+ }
} else {
for(int i=0;i<w;i++) {
// i trust gcc will optimize this loop
diff --git a/tools/editor/plugins/sample_editor_plugin.h b/tools/editor/plugins/sample_editor_plugin.h
index 78d5ed401c..e615667914 100644
--- a/tools/editor/plugins/sample_editor_plugin.h
+++ b/tools/editor/plugins/sample_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/plugins/sample_library_editor_plugin.cpp b/tools/editor/plugins/sample_library_editor_plugin.cpp
index 41c84f6e2c..b497458a2a 100644
--- a/tools/editor/plugins/sample_library_editor_plugin.cpp
+++ b/tools/editor/plugins/sample_library_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -49,9 +49,13 @@ void SampleLibraryEditor::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_TREE) {
play->set_icon( get_icon("Play","EditorIcons") );
+ play->set_tooltip("Play Sample");
stop->set_icon( get_icon("Stop","EditorIcons") );
+ stop->set_tooltip("Stop Sample");
load->set_icon( get_icon("Folder","EditorIcons") );
+ load->set_tooltip("Open Sample File(s)");
_delete->set_icon( get_icon("Del","EditorIcons") );
+ _delete->set_tooltip("Remove Sample");
}
if (p_what==NOTIFICATION_READY) {
@@ -93,7 +97,7 @@ void SampleLibraryEditor::_file_load_request(const DVector<String>& p_path) {
dialog->set_title("Error!");
//dialog->get_cancel()->set_text("Close");
dialog->get_ok()->set_text("Close");
- dialog->popup_centered(Size2(300,60));
+ dialog->popup_centered_minsize();
return; ///beh should show an error i guess
}
String basename = path.get_file().basename();
@@ -235,6 +239,7 @@ void SampleLibraryEditor::_update_library() {
List<StringName> names;
sample_library->get_sample_list(&names);
+ names.sort_custom<StringName::AlphCompare>();
for(List<StringName>::Element *E=names.front();E;E=E->next()) {
@@ -331,7 +336,8 @@ SampleLibraryEditor::SampleLibraryEditor() {
play->set_pos(Point2( 5, 5 ));
play->set_size( Size2(1,1 ) );
play->set_toggle_mode(true);
- //add_child(play);
+ add_child(play);
+ play->hide();
stop = memnew( Button );
@@ -348,13 +354,13 @@ SampleLibraryEditor::SampleLibraryEditor() {
_delete = memnew( Button );
- file = memnew( FileDialog );
+ file = memnew( EditorFileDialog );
add_child(file);
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("Sample",&extensions);
for(int i=0;i<extensions.size();i++)
file->add_filter("*."+extensions[i]);
- file->set_mode(FileDialog::MODE_OPEN_FILES);
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILES);
_delete->set_pos(Point2( 65, 5 ));
_delete->set_size( Size2(1,1 ) );
diff --git a/tools/editor/plugins/sample_library_editor_plugin.h b/tools/editor/plugins/sample_library_editor_plugin.h
index a6ce764b9c..2770ca2d9a 100644
--- a/tools/editor/plugins/sample_library_editor_plugin.h
+++ b/tools/editor/plugins/sample_library_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -54,7 +54,7 @@ class SampleLibraryEditor : public Panel {
Button *_delete;
Tree *tree;
- FileDialog *file;
+ EditorFileDialog *file;
ConfirmationDialog *dialog;
diff --git a/tools/editor/plugins/sample_player_editor_plugin.cpp b/tools/editor/plugins/sample_player_editor_plugin.cpp
index 405107889c..f1c7ca8c98 100644
--- a/tools/editor/plugins/sample_player_editor_plugin.cpp
+++ b/tools/editor/plugins/sample_player_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -94,6 +94,7 @@ void SamplePlayerEditor::_update_sample_library() {
List<StringName> samplenames;
sl->get_sample_list(&samplenames);
+ samplenames.sort_custom<StringName::AlphCompare>();
for(List<StringName>::Element *E=samplenames.front();E;E=E->next()) {
samples->add_item(E->get());
}
diff --git a/tools/editor/plugins/sample_player_editor_plugin.h b/tools/editor/plugins/sample_player_editor_plugin.h
index 4e35e4d8bb..cdd1a99c17 100644
--- a/tools/editor/plugins/sample_player_editor_plugin.h
+++ b/tools/editor/plugins/sample_player_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/plugins/script_editor_plugin.cpp b/tools/editor/plugins/script_editor_plugin.cpp
index 2e5f267d5c..65ed420a51 100644
--- a/tools/editor/plugins/script_editor_plugin.cpp
+++ b/tools/editor/plugins/script_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -38,10 +38,89 @@
#include "os/file_access.h"
#include "scene/main/viewport.h"
#include "os/keyboard.h"
+#include "os/input.h"
+
/*** SCRIPT EDITOR ****/
+class EditorScriptCodeCompletionCache : public ScriptCodeCompletionCache {
+
+
+ struct Cache {
+ uint64_t time_loaded;
+ RES cache;
+ };
+
+ Map<String,Cache> cached;
+
+
+public:
+
+ uint64_t max_time_cache;
+ int max_cache_size;
+
+ void cleanup() {
+
+ List< Map<String,Cache>::Element * > to_clean;
+
+
+ Map<String,Cache>::Element *I=cached.front();
+ while(I) {
+ if ((OS::get_singleton()->get_ticks_msec()-I->get().time_loaded)>max_time_cache) {
+ to_clean.push_back(I);
+ }
+ I=I->next();
+ }
+
+ while(to_clean.front()) {
+ cached.erase(to_clean.front()->get());
+ to_clean.pop_front();
+ }
+ }
+
+ RES get_cached_resource(const String& p_path) {
+
+ Map<String,Cache>::Element *E=cached.find(p_path);
+ if (!E) {
+
+ Cache c;
+ c.cache=ResourceLoader::load(p_path);
+ E=cached.insert(p_path,c);
+ }
+
+ E->get().time_loaded=OS::get_singleton()->get_ticks_msec();
+
+ if (cached.size()>max_cache_size) {
+ uint64_t older;
+ Map<String,Cache>::Element *O=cached.front();
+ older=O->get().time_loaded;
+ Map<String,Cache>::Element *I=O;
+ while(I) {
+ if (I->get().time_loaded<older) {
+ older = I->get().time_loaded;
+ O=I;
+ }
+ I=I->next();
+ }
+ if (O!=E) {//should never heppane..
+ cached.erase(O);
+ }
+ }
+
+ return E->get().cache;
+ }
+
+
+ EditorScriptCodeCompletionCache() {
+
+ max_cache_size=128;
+ max_time_cache=5*60*1000; //minutes, five
+ }
+
+};
+
+#define SORT_SCRIPT_LIST
void ScriptEditorQuickOpen::popup(const Vector<String>& p_functions, bool p_dontclear) {
@@ -118,6 +197,8 @@ void ScriptEditorQuickOpen::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_TREE) {
connect("confirmed",this,"_confirmed");
+
+
}
}
@@ -188,7 +269,7 @@ void ScriptTextEditor::apply_code() {
if (script.is_null())
return;
- print_line("applying code");
+// print_line("applying code");
script->set_source_code(get_text_edit()->get_text());
script->update_exports();
}
@@ -210,6 +291,7 @@ void ScriptTextEditor::_load_theme_settings() {
get_text_edit()->add_color_override("font_selected_color",EDITOR_DEF("text_editor/text_selected_color",Color(1,1,1)));
get_text_edit()->add_color_override("selection_color",EDITOR_DEF("text_editor/selection_color",Color(0.2,0.2,1)));
get_text_edit()->add_color_override("brace_mismatch_color",EDITOR_DEF("text_editor/brace_mismatch_color",Color(1,0.2,0.2)));
+ get_text_edit()->add_color_override("current_line_color",EDITOR_DEF("text_editor/current_line_color",Color(0.3,0.5,0.8,0.15)));
Color keyword_color= EDITOR_DEF("text_editor/keyword_color",Color(0.5,0.0,0.2));
@@ -238,10 +320,10 @@ void ScriptTextEditor::_load_theme_settings() {
//colorize engine types
Color type_color= EDITOR_DEF("text_editor/engine_type_color",Color(0.0,0.2,0.4));
- List<String> types;
+ List<StringName> types;
ObjectTypeDB::get_type_list(&types);
- for(List<String>::Element *E=types.front();E;E=E->next()) {
+ for(List<StringName>::Element *E=types.front();E;E=E->next()) {
get_text_edit()->add_keyword_color(E->get(),type_color);
}
@@ -284,8 +366,19 @@ void ScriptTextEditor::reload_text() {
ERR_FAIL_COND(script.is_null()) ;
- get_text_edit()->set_text(script->get_source_code());
- get_text_edit()->clear_undo_history();
+ TextEdit *te = get_text_edit();
+ int column = te->cursor_get_column();
+ int row = te->cursor_get_line();
+ int h = te->get_h_scroll();
+ int v = te->get_v_scroll();
+
+ te->set_text(script->get_source_code());
+ te->clear_undo_history();
+ te->cursor_set_line(row);
+ te->cursor_set_column(column);
+ te->set_h_scroll(h);
+ te->set_v_scroll(v);
+
_line_col_changed();
}
@@ -294,12 +387,11 @@ void ScriptTextEditor::_notification(int p_what) {
if (p_what==NOTIFICATION_READY) {
- _update_name();
+ //emit_signal("name_changed");
}
}
-void ScriptTextEditor::_update_name() {
-
+String ScriptTextEditor::get_name() {
String name;
if (script->get_path().find("local://")==-1 && script->get_path().find("::")==-1) {
@@ -312,21 +404,20 @@ void ScriptTextEditor::_update_name() {
else
name=script->get_type()+"("+itos(script->get_instance_ID())+")";
+ return name;
- if (name!=String(get_name())) {
+}
- set_name(name);
+Ref<Texture> ScriptTextEditor::get_icon() {
+ if (get_parent_control() && get_parent_control()->has_icon(script->get_type(),"EditorIcons")) {
+ return get_parent_control()->get_icon(script->get_type(),"EditorIcons");
}
- if (!has_meta("_tab_icon")) {
- if (get_parent_control() && get_parent_control()->has_icon(script->get_type(),"EditorIcons")) {
- set_meta("_tab_icon",get_parent_control()->get_icon(script->get_type(),"EditorIcons"));
- }
- }
+ return Ref<Texture>();
+}
-}
void ScriptTextEditor::set_edited_script(const Ref<Script>& p_script) {
@@ -342,8 +433,7 @@ void ScriptTextEditor::set_edited_script(const Ref<Script>& p_script) {
get_text_edit()->tag_saved_version();
- _update_name();
-
+ emit_signal("name_changed");
_line_col_changed();
}
@@ -382,7 +472,7 @@ void ScriptTextEditor::_validate_script() {
te->set_line_as_marked(i,line==i);
}
- _update_name();
+ emit_signal("name_changed");
}
@@ -416,6 +506,10 @@ void ScriptTextEditor::_code_complete_script(const String& p_code, List<String>*
}
}
+void ScriptTextEditor::_bind_methods() {
+
+ ADD_SIGNAL(MethodInfo("name_changed"));
+}
ScriptTextEditor::ScriptTextEditor() {
@@ -452,6 +546,10 @@ void ScriptEditor::_show_debugger(bool p_show) {
}
+void ScriptEditor::_script_created(Ref<Script> p_script) {
+ editor->push_item(p_script.operator->());
+}
+
void ScriptEditor::_goto_script_line2(int p_line) {
int selected = tab_container->get_current_tab();
@@ -474,27 +572,122 @@ void ScriptEditor::_goto_script_line(REF p_script,int p_line) {
}
+
+void ScriptEditor::_update_history_arrows() {
+
+ script_back->set_disabled( history_pos<=0 );
+ script_forward->set_disabled( history_pos>=history.size()-1 );
+}
+
+
+void ScriptEditor::_go_to_tab(int p_idx) {
+
+ Node *cn = tab_container->get_child(p_idx);
+ if (!cn)
+ return;
+ Control *c = cn->cast_to<Control>();
+ if (!c)
+ return;
+
+ if (history_pos>=0 && history_pos<history.size() && history[history_pos].control==tab_container->get_current_tab_control()) {
+
+ Node *n = tab_container->get_current_tab_control();
+
+ if (n->cast_to<ScriptTextEditor>()) {
+
+ history[history_pos].scroll_pos=n->cast_to<ScriptTextEditor>()->get_text_edit()->get_v_scroll();
+ history[history_pos].cursor_column=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_column();
+ history[history_pos].cursor_row=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_line();
+ }
+ if (n->cast_to<EditorHelp>()) {
+
+ history[history_pos].scroll_pos=n->cast_to<EditorHelp>()->get_scroll();
+ }
+ }
+
+ history.resize(history_pos+1);
+ ScriptHistory sh;
+ sh.control=c;
+ sh.scroll_pos=0;
+
+ history.push_back(sh);
+ history_pos++;
+
+
+ tab_container->set_current_tab(p_idx);
+
+ c = tab_container->get_current_tab_control();
+
+ if (c->cast_to<ScriptTextEditor>()) {
+
+ script_name_label->set_text(c->cast_to<ScriptTextEditor>()->get_name());
+ script_icon->set_texture(c->cast_to<ScriptTextEditor>()->get_icon());
+ if (is_visible())
+ c->cast_to<ScriptTextEditor>()->get_text_edit()->grab_focus();
+ }
+ if (c->cast_to<EditorHelp>()) {
+
+ script_name_label->set_text(c->cast_to<EditorHelp>()->get_class_name());
+ script_icon->set_texture(get_icon("Help","EditorIcons"));
+ if (is_visible())
+ c->cast_to<EditorHelp>()->set_focused();
+ }
+
+
+
+ c->set_meta("__editor_pass",++edit_pass);
+ _update_history_arrows();
+ _update_script_colors();
+}
+
void ScriptEditor::_close_current_tab() {
int selected = tab_container->get_current_tab();
if (selected<0 || selected>=tab_container->get_child_count())
return;
-
+
+ Node *tselected = tab_container->get_child(selected);
ScriptTextEditor *current = tab_container->get_child(selected)->cast_to<ScriptTextEditor>();
- if (!current)
- return;
+ if (current) {
+ apply_scripts();
+ }
- apply_scripts();
+ //remove from history
+ history.resize(history_pos+1);
+
+ for(int i=0;i<history.size();i++) {
+ if (history[i].control==tselected) {
+ history.remove(i);
+ i--;
+ history_pos--;
+ }
+ }
+
+ if (history_pos>=history.size()) {
+ history_pos=history.size()-1;
+ }
int idx = tab_container->get_current_tab();
- memdelete(current);
+ memdelete(tselected);
if (idx>=tab_container->get_child_count())
idx=tab_container->get_child_count()-1;
- if (idx>=0)
+ if (idx>=0) {
+
+ if (history_pos>=0) {
+ idx = history[history_pos].control->get_index();
+ }
tab_container->set_current_tab(idx);
- _update_window_menu();
- _save_files_state();
+ //script_list->select(idx);
+ }
+
+
+ _update_history_arrows();
+
+
+
+ _update_script_names();
+ EditorNode::get_singleton()->save_layout();
}
@@ -585,10 +778,10 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource>& p_res) {
ste->get_text_edit()->tag_saved_version();
}
- ste->_update_name();
-
}
+ _update_script_names();
+
}
bool ScriptEditor::_test_script_times_on_disk() {
@@ -629,8 +822,13 @@ bool ScriptEditor::_test_script_times_on_disk() {
- if (!all_ok)
- disk_changed->call_deferred("popup_centered_ratio",0.5);
+ if (!all_ok) {
+ if (bool(EDITOR_DEF("text_editor/auto_reload_changed_scripts",false))) {
+ script_editor->_reload_scripts();
+ } else {
+ disk_changed->call_deferred("popup_centered_ratio",0.5);
+ }
+ }
return all_ok;
}
@@ -648,31 +846,15 @@ void ScriptEditor::swap_lines(TextEdit *tx, int line1, int line2)
void ScriptEditor::_menu_option(int p_option) {
- if (p_option==FILE_OPEN) {
-
- editor->open_resource("Script");
- return;
- }
- int selected = tab_container->get_current_tab();
- if (selected<0 || selected>=tab_container->get_child_count())
- return;
-
- ScriptTextEditor *current = tab_container->get_child(selected)->cast_to<ScriptTextEditor>();
- if (!current)
- return;
-
switch(p_option) {
- case FILE_SAVE: {
-
- if (!_test_script_times_on_disk())
- return;
- editor->save_resource( current->get_edited_script() );
-
+ case FILE_NEW: {
+ script_create_dialog->config("Node", ".gd");
+ script_create_dialog->popup_centered(Size2(300, 300));
} break;
- case FILE_SAVE_AS: {
-
- editor->save_resource_as( current->get_edited_script() );
+ case FILE_OPEN: {
+ editor->open_resource("Script");
+ return;
} break;
case FILE_SAVE_ALL: {
@@ -697,373 +879,470 @@ void ScriptEditor::_menu_option(int p_option) {
} break;
- case EDIT_UNDO: {
- current->get_text_edit()->undo();
- } break;
- case EDIT_REDO: {
- current->get_text_edit()->redo();
- } break;
- case EDIT_CUT: {
-
- current->get_text_edit()->cut();
- } break;
- case EDIT_COPY: {
- current->get_text_edit()->copy();
-
- } break;
- case EDIT_PASTE: {
- current->get_text_edit()->paste();
+ case SEARCH_HELP: {
+ help_search_dialog->popup("current");
} break;
- case EDIT_SELECT_ALL: {
-
- current->get_text_edit()->select_all();
+ case SEARCH_CLASSES: {
- } break;
- case EDIT_MOVE_LINE_UP: {
-
- TextEdit *tx = current->get_text_edit();
- Ref<Script> scr = current->get_edited_script();
- if (scr.is_null())
- return;
-
- if (tx->is_selection_active())
- {
- int from_line = tx->get_selection_from_line();
- int from_col = tx->get_selection_from_column();
- int to_line = tx->get_selection_to_line();
- int to_column = tx->get_selection_to_column();
-
- for (int i = from_line; i <= to_line; i++)
- {
- int line_id = i;
- int next_id = i - 1;
-
- if (line_id == 0 || next_id < 0)
- return;
-
- swap_lines(tx, line_id, next_id);
- }
- int from_line_up = from_line > 0 ? from_line-1 : from_line;
- int to_line_up = to_line > 0 ? to_line-1 : to_line;
- tx->select(from_line_up, from_col, to_line_up, to_column);
- }
- else
- {
- int line_id = tx->cursor_get_line();
- int next_id = line_id - 1;
-
- if (line_id == 0 || next_id < 0)
- return;
-
- swap_lines(tx, line_id, next_id);
- }
- tx->update();
-
- } break;
- case EDIT_MOVE_LINE_DOWN: {
-
- TextEdit *tx = current->get_text_edit();
- Ref<Script> scr = current->get_edited_script();
- if (scr.is_null())
- return;
-
- if (tx->is_selection_active())
- {
- int from_line = tx->get_selection_from_line();
- int from_col = tx->get_selection_from_column();
- int to_line = tx->get_selection_to_line();
- int to_column = tx->get_selection_to_column();
-
- for (int i = to_line; i >= from_line; i--)
- {
- int line_id = i;
- int next_id = i + 1;
-
- if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count())
- return;
-
- swap_lines(tx, line_id, next_id);
- }
- int from_line_down = from_line < tx->get_line_count() ? from_line+1 : from_line;
- int to_line_down = to_line < tx->get_line_count() ? to_line+1 : to_line;
- tx->select(from_line_down, from_col, to_line_down, to_column);
- }
- else
- {
- int line_id = tx->cursor_get_line();
- int next_id = line_id + 1;
-
- if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count())
- return;
-
- swap_lines(tx, line_id, next_id);
- }
- tx->update();
-
- } break;
- case EDIT_INDENT_LEFT: {
-
- TextEdit *tx = current->get_text_edit();
- Ref<Script> scr = current->get_edited_script();
- if (scr.is_null())
- return;
-
- int begin, end;
- begin = tx->get_selection_from_line();
- if (tx->is_selection_active())
- {
- end = tx->get_selection_to_line();
- for (int i = begin; i <= end; i++)
- {
- String line_text = tx->get_line(i);
- // begins with tab
- if (line_text.begins_with("\t"))
- {
- line_text = line_text.substr(1, line_text.length());
- tx->set_line(i, line_text);
- }
- // begins with 4 spaces
- else if (line_text.begins_with(" "))
- {
- line_text = line_text.substr(4, line_text.length());
- tx->set_line(i, line_text);
- }
- }
- }
- else
- {
- begin = tx->cursor_get_line();
- String line_text = tx->get_line(begin);
- // begins with tab
- if (line_text.begins_with("\t"))
- {
- line_text = line_text.substr(1, line_text.length());
- tx->set_line(begin, line_text);
- }
- // begins with 4 spaces
- else if (line_text.begins_with(" "))
- {
- line_text = line_text.substr(4, line_text.length());
- tx->set_line(begin, line_text);
- }
- }
- tx->update();
- //tx->deselect();
-
- } break;
- case EDIT_INDENT_RIGHT: {
-
- TextEdit *tx = current->get_text_edit();
- Ref<Script> scr = current->get_edited_script();
- if (scr.is_null())
- return;
-
- int begin, end;
- begin = tx->get_selection_from_line();
- if (tx->is_selection_active())
- {
- end = tx->get_selection_to_line();
- for (int i = begin; i <= end; i++)
- {
- String line_text = tx->get_line(i);
- line_text = '\t' + line_text;
- tx->set_line(i, line_text);
- }
- }
- else
- {
- begin = tx->cursor_get_line();
- String line_text = tx->get_line(begin);
- line_text = '\t' + line_text;
- tx->set_line(begin, line_text);
- }
- tx->update();
- //tx->deselect();
-
- } break;
- case EDIT_CLONE_DOWN: {
-
- TextEdit *tx = current->get_text_edit();
- Ref<Script> scr = current->get_edited_script();
- if (scr.is_null())
- return;
- int line = tx->cursor_get_line();
- int next_line = line + 1;
-
- if (line == tx->get_line_count() || next_line > tx->get_line_count())
- return;
-
- String line_clone = tx->get_line(line);
- tx->insert_at(line_clone, next_line);
- tx->update();
-
- } break;
- case EDIT_TOGGLE_COMMENT: {
-
- TextEdit *tx = current->get_text_edit();
- Ref<Script> scr = current->get_edited_script();
- if (scr.is_null())
- return;
-
- int begin, end;
- begin = tx->get_selection_from_line();
- if (tx->is_selection_active())
- {
- end = tx->get_selection_to_line();
- for (int i = begin; i <= end; i++)
- {
- String line_text = tx->get_line(i);
-
- if (line_text.begins_with("#"))
- line_text = line_text.strip_edges().substr(1, line_text.length());
- else
- line_text = "#" + line_text;
- tx->set_line(i, line_text);
- }
- }
- else
- {
- begin = tx->cursor_get_line();
- String line_text = tx->get_line(begin);
-
- if (line_text.begins_with("#"))
- line_text = line_text.strip_edges().substr(1, line_text.length());
- else
- line_text = "#" + line_text;
- tx->set_line(begin, line_text);
- }
- tx->update();
- //tx->deselect();
-
- } break;
- case EDIT_COMPLETE: {
-
- current->get_text_edit()->query_code_comple();
+ if (tab_container->get_tab_count()==0)
+ break;
- } break;
- case EDIT_AUTO_INDENT: {
+ String current;
- TextEdit *te = current->get_text_edit();
- String text = te->get_text();
- Ref<Script> scr = current->get_edited_script();
- if (scr.is_null())
- return;
- int begin,end;
- if (te->is_selection_active()) {
- begin=te->get_selection_from_line();
- end=te->get_selection_to_line();
- } else {
- begin=0;
- end=te->get_line_count()-1;
+ EditorHelp *eh = tab_container->get_child( tab_container->get_current_tab() )->cast_to<EditorHelp>();
+ if (eh) {
+ current=eh->get_class_name();
}
- scr->get_language()->auto_indent_code(text,begin,end);
- te->set_text(text);
+ help_index->popup_centered_ratio(0.6);
+ if (current!="") {
+ help_index->call_deferred("select_class",current);
+ }
} break;
- case SEARCH_FIND: {
-
- find_replace_dialog->set_text_edit(current->get_text_edit());
- find_replace_dialog->popup_search();
- } break;
- case SEARCH_FIND_NEXT: {
-
- find_replace_dialog->set_text_edit(current->get_text_edit());
- find_replace_dialog->search_next();
- } break;
- case SEARCH_REPLACE: {
+ case SEARCH_WEBSITE: {
- find_replace_dialog->set_text_edit(current->get_text_edit());
- find_replace_dialog->popup_replace();
+ OS::get_singleton()->shell_open("http://www.godotengine.org/projects/godot-engine/wiki/Documentation#Tutorials");
} break;
- case SEARCH_LOCATE_FUNCTION: {
- if (!current)
- return;
- quick_open->popup(current->get_functions());
- } break;
- case SEARCH_GOTO_LINE: {
+ case WINDOW_NEXT: {
- goto_line_dialog->popup_find_line(current->get_text_edit());
+ _history_forward();
} break;
- case DEBUG_TOGGLE_BREAKPOINT: {
- int line=current->get_text_edit()->cursor_get_line();
- bool dobreak = !current->get_text_edit()->is_line_set_as_breakpoint(line);
- current->get_text_edit()->set_line_as_breakpoint(line,dobreak);
+ case WINDOW_PREV: {
+ _history_back();
} break;
- case DEBUG_NEXT: {
- if (debugger)
- debugger->debug_next();
- } break;
- case DEBUG_STEP: {
-
- if (debugger)
- debugger->debug_step();
-
- } break;
- case DEBUG_BREAK: {
-
- if (debugger)
- debugger->debug_break();
+ }
- } break;
- case DEBUG_CONTINUE: {
- if (debugger)
- debugger->debug_continue();
+ int selected = tab_container->get_current_tab();
+ if (selected<0 || selected>=tab_container->get_child_count())
+ return;
- } break;
- case DEBUG_SHOW: {
- if (debugger) {
- bool visible = debug_menu->get_popup()->is_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW) );
- debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), !visible);
- if (visible)
- debugger->hide();
+ ScriptTextEditor *current = tab_container->get_child(selected)->cast_to<ScriptTextEditor>();
+ if (current) {
+
+ switch(p_option) {
+ case FILE_NEW: {
+ script_create_dialog->config("Node", ".gd");
+ script_create_dialog->popup_centered(Size2(300, 300));
+ } break;
+ case FILE_SAVE: {
+ if (!_test_script_times_on_disk())
+ return;
+ editor->save_resource( current->get_edited_script() );
+
+ } break;
+ case FILE_SAVE_AS: {
+
+ editor->save_resource_as( current->get_edited_script() );
+
+ } break;
+ case EDIT_UNDO: {
+ current->get_text_edit()->undo();
+ current->get_text_edit()->call_deferred("grab_focus");
+ } break;
+ case EDIT_REDO: {
+ current->get_text_edit()->redo();
+ current->get_text_edit()->call_deferred("grab_focus");
+ } break;
+ case EDIT_CUT: {
+
+ current->get_text_edit()->cut();
+ current->get_text_edit()->call_deferred("grab_focus");
+ } break;
+ case EDIT_COPY: {
+ current->get_text_edit()->copy();
+ current->get_text_edit()->call_deferred("grab_focus");
+
+ } break;
+ case EDIT_PASTE: {
+ current->get_text_edit()->paste();
+ current->get_text_edit()->call_deferred("grab_focus");
+
+ } break;
+ case EDIT_SELECT_ALL: {
+
+ current->get_text_edit()->select_all();
+ current->get_text_edit()->call_deferred("grab_focus");
+
+ } break;
+ case EDIT_MOVE_LINE_UP: {
+
+ TextEdit *tx = current->get_text_edit();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+
+ if (tx->is_selection_active())
+ {
+ int from_line = tx->get_selection_from_line();
+ int from_col = tx->get_selection_from_column();
+ int to_line = tx->get_selection_to_line();
+ int to_column = tx->get_selection_to_column();
+
+ for (int i = from_line; i <= to_line; i++)
+ {
+ int line_id = i;
+ int next_id = i - 1;
+
+ if (line_id == 0 || next_id < 0)
+ return;
+
+ swap_lines(tx, line_id, next_id);
+ }
+ int from_line_up = from_line > 0 ? from_line-1 : from_line;
+ int to_line_up = to_line > 0 ? to_line-1 : to_line;
+ tx->select(from_line_up, from_col, to_line_up, to_column);
+ }
else
- debugger->show();
- }
- } break;
- case HELP_CONTEXTUAL: {
- String text = current->get_text_edit()->get_selection_text();
- if (text == "")
- text = current->get_text_edit()->get_word_under_cursor();
- if (text != "")
- editor->emit_signal("request_help", text);
- } break;
- case WINDOW_CLOSE: {
-
- erase_tab_confirm->set_text("Close Tab?:\n\""+current->get_name()+"\"");
- erase_tab_confirm->popup_centered(Point2(250,80));
- } break;
- case WINDOW_MOVE_LEFT: {
-
- if (tab_container->get_current_tab()>0) {
- tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()-1);
- tab_container->move_child(current,tab_container->get_current_tab()-1);
- _update_window_menu();
+ {
+ int line_id = tx->cursor_get_line();
+ int next_id = line_id - 1;
+
+ if (line_id == 0 || next_id < 0)
+ return;
+
+ swap_lines(tx, line_id, next_id);
+ }
+ tx->update();
+
+ } break;
+ case EDIT_MOVE_LINE_DOWN: {
+
+ TextEdit *tx = current->get_text_edit();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+
+ if (tx->is_selection_active())
+ {
+ int from_line = tx->get_selection_from_line();
+ int from_col = tx->get_selection_from_column();
+ int to_line = tx->get_selection_to_line();
+ int to_column = tx->get_selection_to_column();
+
+ for (int i = to_line; i >= from_line; i--)
+ {
+ int line_id = i;
+ int next_id = i + 1;
+
+ if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count())
+ return;
+
+ swap_lines(tx, line_id, next_id);
+ }
+ int from_line_down = from_line < tx->get_line_count() ? from_line+1 : from_line;
+ int to_line_down = to_line < tx->get_line_count() ? to_line+1 : to_line;
+ tx->select(from_line_down, from_col, to_line_down, to_column);
+ }
+ else
+ {
+ int line_id = tx->cursor_get_line();
+ int next_id = line_id + 1;
+
+ if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count())
+ return;
+
+ swap_lines(tx, line_id, next_id);
+ }
+ tx->update();
+
+ } break;
+ case EDIT_INDENT_LEFT: {
+
+ TextEdit *tx = current->get_text_edit();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+
+
+ if (tx->is_selection_active())
+ {
+ int begin = tx->get_selection_from_line();
+ int end = tx->get_selection_to_line();
+ for (int i = begin; i <= end; i++)
+ {
+ String line_text = tx->get_line(i);
+ // begins with tab
+ if (line_text.begins_with("\t"))
+ {
+ line_text = line_text.substr(1, line_text.length());
+ tx->set_line(i, line_text);
+ }
+ // begins with 4 spaces
+ else if (line_text.begins_with(" "))
+ {
+ line_text = line_text.substr(4, line_text.length());
+ tx->set_line(i, line_text);
+ }
+ }
+ }
+ else
+ {
+ int begin = tx->cursor_get_line();
+ String line_text = tx->get_line(begin);
+ // begins with tab
+ if (line_text.begins_with("\t"))
+ {
+ line_text = line_text.substr(1, line_text.length());
+ tx->set_line(begin, line_text);
+ }
+ // begins with 4 spaces
+ else if (line_text.begins_with(" "))
+ {
+ line_text = line_text.substr(4, line_text.length());
+ tx->set_line(begin, line_text);
+ }
+ }
+ tx->update();
+ //tx->deselect();
+
+ } break;
+ case EDIT_INDENT_RIGHT: {
+
+ TextEdit *tx = current->get_text_edit();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+
+ if (tx->is_selection_active())
+ {
+ int begin = tx->get_selection_from_line();
+ int end = tx->get_selection_to_line();
+ for (int i = begin; i <= end; i++)
+ {
+ String line_text = tx->get_line(i);
+ line_text = '\t' + line_text;
+ tx->set_line(i, line_text);
+ }
+ }
+ else
+ {
+ int begin = tx->cursor_get_line();
+ String line_text = tx->get_line(begin);
+ line_text = '\t' + line_text;
+ tx->set_line(begin, line_text);
+ }
+ tx->update();
+ //tx->deselect();
+
+ } break;
+ case EDIT_CLONE_DOWN: {
+
+ TextEdit *tx = current->get_text_edit();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+ int line = tx->cursor_get_line();
+ int next_line = line + 1;
+
+ if (line == tx->get_line_count() || next_line > tx->get_line_count())
+ return;
+
+ String line_clone = tx->get_line(line);
+ tx->insert_at(line_clone, next_line);
+ tx->update();
+
+ } break;
+ case EDIT_TOGGLE_COMMENT: {
+
+ TextEdit *tx = current->get_text_edit();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+
+
+
+ if (tx->is_selection_active())
+ {
+ int begin = tx->get_selection_from_line();
+ int end = tx->get_selection_to_line();
+ for (int i = begin; i <= end; i++)
+ {
+ String line_text = tx->get_line(i);
+
+ if (line_text.begins_with("#"))
+ line_text = line_text.substr(1, line_text.length());
+ else
+ line_text = "#" + line_text;
+ tx->set_line(i, line_text);
+ }
+ }
+ else
+ {
+ int begin = tx->cursor_get_line();
+ String line_text = tx->get_line(begin);
+
+ if (line_text.begins_with("#"))
+ line_text = line_text.substr(1, line_text.length());
+ else
+ line_text = "#" + line_text;
+ tx->set_line(begin, line_text);
+ }
+ tx->update();
+ //tx->deselect();
+
+ } break;
+ case EDIT_COMPLETE: {
+
+ current->get_text_edit()->query_code_comple();
+
+ } break;
+ case EDIT_AUTO_INDENT: {
+
+ TextEdit *te = current->get_text_edit();
+ String text = te->get_text();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+ int begin,end;
+ if (te->is_selection_active()) {
+ begin=te->get_selection_from_line();
+ end=te->get_selection_to_line();
+ } else {
+ begin=0;
+ end=te->get_line_count()-1;
+ }
+ scr->get_language()->auto_indent_code(text,begin,end);
+ te->set_text(text);
+
+
+ } break;
+ case SEARCH_FIND: {
+
+ find_replace_dialog->set_text_edit(current->get_text_edit());
+ find_replace_dialog->popup_search();
+ } break;
+ case SEARCH_FIND_NEXT: {
+
+ find_replace_dialog->set_text_edit(current->get_text_edit());
+ find_replace_dialog->search_next();
+ } break;
+ case SEARCH_REPLACE: {
+
+ find_replace_dialog->set_text_edit(current->get_text_edit());
+ find_replace_dialog->popup_replace();
+ } break;
+ case SEARCH_LOCATE_FUNCTION: {
+
+ if (!current)
+ return;
+ quick_open->popup(current->get_functions());
+ } break;
+ case SEARCH_GOTO_LINE: {
+
+ goto_line_dialog->popup_find_line(current->get_text_edit());
+ } break;
+ case DEBUG_TOGGLE_BREAKPOINT: {
+ int line=current->get_text_edit()->cursor_get_line();
+ bool dobreak = !current->get_text_edit()->is_line_set_as_breakpoint(line);
+ current->get_text_edit()->set_line_as_breakpoint(line,dobreak);
+ get_debugger()->set_breakpoint(current->get_edited_script()->get_path(),line+1,dobreak);
+ } break;
+ case DEBUG_NEXT: {
+
+ if (debugger)
+ debugger->debug_next();
+ } break;
+ case DEBUG_STEP: {
+
+ if (debugger)
+ debugger->debug_step();
+
+ } break;
+ case DEBUG_BREAK: {
+
+ if (debugger)
+ debugger->debug_break();
+
+ } break;
+ case DEBUG_CONTINUE: {
+
+ if (debugger)
+ debugger->debug_continue();
+
+ } break;
+ case DEBUG_SHOW: {
+ if (debugger) {
+ bool visible = debug_menu->get_popup()->is_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW) );
+ debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), !visible);
+ if (visible)
+ debugger->hide();
+ else
+ debugger->show();
+ }
+ } break;
+ case HELP_CONTEXTUAL: {
+ String text = current->get_text_edit()->get_selection_text();
+ if (text == "")
+ text = current->get_text_edit()->get_word_under_cursor();
+ if (text != "")
+ help_search_dialog->popup(text);
+ } break;
+ case FILE_CLOSE: {
+ if (current->get_text_edit()->get_version()!=current->get_text_edit()->get_saved_version()) {
+ erase_tab_confirm->set_text("Close and save changes?\n\""+current->get_name()+"\"");
+ erase_tab_confirm->popup_centered_minsize();
+ } else {
+ _close_current_tab();
+ }
+ } break;
+ case WINDOW_MOVE_LEFT: {
+
+ if (tab_container->get_current_tab()>0) {
+ tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()-1);
+ script_list->call_deferred("select",tab_container->get_current_tab()-1);
+ tab_container->move_child(current,tab_container->get_current_tab()-1);
+ _update_script_names();
+ }
+ } break;
+ case WINDOW_MOVE_RIGHT: {
+
+ if (tab_container->get_current_tab()<tab_container->get_child_count()-1) {
+ tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()+1);
+ script_list->call_deferred("select",tab_container->get_current_tab()+1);
+ tab_container->move_child(current,tab_container->get_current_tab()+1);
+ _update_script_names();
+ }
+
+
+ } break;
+
+ default: {
+
+ if (p_option>=WINDOW_SELECT_BASE) {
+
+ tab_container->set_current_tab(p_option-WINDOW_SELECT_BASE);
+ script_list->select(p_option-WINDOW_SELECT_BASE);
+
+ }
}
- } break;
- case WINDOW_MOVE_RIGHT: {
+ }
+ }
- if (tab_container->get_current_tab()<tab_container->get_child_count()-1) {
- tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()+1);
- tab_container->move_child(current,tab_container->get_current_tab()+1);
- _update_window_menu();
- }
+ EditorHelp *help = tab_container->get_child(selected)->cast_to<EditorHelp>();
+ if (help) {
+ switch(p_option) {
- } break;
- default: {
+ case SEARCH_FIND: {
+ help->popup_search();
+ } break;
+ case SEARCH_FIND_NEXT: {
+ help->search_again();
+ } break;
+ case FILE_CLOSE: {
+ _close_current_tab();
+ } break;
- if (p_option>=WINDOW_SELECT_BASE) {
- tab_container->set_current_tab(p_option-WINDOW_SELECT_BASE);
- }
}
}
+
}
void ScriptEditor::_tab_changed(int p_which) {
@@ -1080,12 +1359,35 @@ void ScriptEditor::_notification(int p_what) {
editor->connect("stop_pressed",this,"_editor_stop");
editor->connect("script_add_function_request",this,"_add_callback");
editor->connect("resource_saved",this,"_res_saved_callback");
+ script_list->connect("item_selected",this,"_script_selected");
+ script_split->connect("dragged",this,"_script_split_dragged");
+ autosave_timer->connect("timeout",this,"_autosave_scripts");
+ {
+ float autosave_time = EditorSettings::get_singleton()->get("text_editor/autosave_interval_secs");
+ if (autosave_time>0) {
+ autosave_timer->set_wait_time(autosave_time);
+ autosave_timer->start();
+ } else {
+ autosave_timer->stop();
+ }
+ }
+
+ EditorSettings::get_singleton()->connect("settings_changed",this,"_editor_settings_changed");
+ help_search->set_icon(get_icon("Help","EditorIcons"));
+ site_search->set_icon(get_icon("Godot","EditorIcons"));
+ class_search->set_icon(get_icon("ClassList","EditorIcons"));
+
+ script_forward->set_icon(get_icon("Forward","EditorIcons"));
+ script_back->set_icon(get_icon("Back","EditorIcons"));
+
+
}
if (p_what==NOTIFICATION_READY) {
- _update_window_menu();
+
+ get_tree()->connect("tree_changed",this,"_tree_changed");
}
if (p_what==NOTIFICATION_EXIT_TREE) {
@@ -1125,10 +1427,11 @@ static const Node * _find_node_with_script(const Node* p_node, const RefPtr & p_
Dictionary ScriptEditor::get_state() const {
- apply_scripts();
- Dictionary state;
+// apply_scripts();
+ Dictionary state;
+#if 0
Array paths;
int open=-1;
@@ -1161,12 +1464,12 @@ Dictionary ScriptEditor::get_state() const {
if (open!=-1)
state["current"]=open;
-
+#endif
return state;
}
void ScriptEditor::set_state(const Dictionary& p_state) {
-
+#if 0
print_line("attempt set state: "+String(Variant(p_state)));
if (!p_state.has("sources"))
@@ -1203,10 +1506,11 @@ void ScriptEditor::set_state(const Dictionary& p_state) {
if (p_state.has("current")) {
tab_container->set_current_tab(p_state["current"]);
}
+#endif
}
void ScriptEditor::clear() {
-
+#if 0
List<ScriptTextEditor*> stes;
for(int i=0;i<tab_container->get_child_count();i++) {
@@ -1226,24 +1530,18 @@ void ScriptEditor::clear() {
int idx = tab_container->get_current_tab();
if (idx>=tab_container->get_child_count())
idx=tab_container->get_child_count()-1;
- if (idx>=0)
+ if (idx>=0) {
tab_container->set_current_tab(idx);
+ script_list->select( script_list->find_metadata(idx) );
+ }
- _update_window_menu();
+#endif
}
-void ScriptEditor::_save_files_state() {
-
- return; //no thank you
-
- String rpath="_open_scripts_"+Globals::get_singleton()->get_resource_path();
- rpath=rpath.replace("\\","_-_");
- rpath=rpath.replace("/","_-_");
- rpath=rpath.replace(":","_");
- Vector<String> scripts;
+void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
for(int i=0;i<tab_container->get_child_count();i++) {
@@ -1251,114 +1549,236 @@ void ScriptEditor::_save_files_state() {
if (!ste)
continue;
+ List<int> bpoints;
+ ste->get_text_edit()->get_breakpoints(&bpoints);
Ref<Script> script = ste->get_edited_script();
- if (script->get_path()!="" && script->get_path().find("local://")==-1 && script->get_path().find("::")==-1) {
+ String base = script->get_path();
+ ERR_CONTINUE( base.begins_with("local://") || base=="" );
+ for(List<int>::Element *E=bpoints.front();E;E=E->next()) {
- scripts.push_back(script->get_path());
+ p_breakpoints->push_back(base+":"+itos(E->get()+1));
}
}
+}
+
+
+
- EditorSettings::get_singleton()->set(rpath,scripts);
- EditorSettings::get_singleton()->save();
+void ScriptEditor::ensure_focus_current() {
+
+ if (!is_inside_tree())
+ return;
+
+ int cidx = tab_container->get_current_tab();
+ if (cidx<0 || cidx>=tab_container->get_tab_count());
+ Control *c = tab_container->get_child(cidx)->cast_to<Control>();
+ if (!c)
+ return;
+ ScriptTextEditor *ste = c->cast_to<ScriptTextEditor>();
+ if (!ste)
+ return;
+ ste->get_text_edit()->grab_focus();
}
-void ScriptEditor::_load_files_state() {
- return;
+void ScriptEditor::_script_selected(int p_idx) {
- String rpath="_open_scripts_"+Globals::get_singleton()->get_resource_path();
- rpath=rpath.replace("\\","_-_");
- rpath=rpath.replace("/","_-_");
- rpath=rpath.replace(":","_");
+ grab_focus_block = !Input::get_singleton()->is_mouse_button_pressed(1); //amazing hack, simply amazing
- if (EditorSettings::get_singleton()->has(rpath)) {
+ _go_to_tab(script_list->get_item_metadata(p_idx));
+ grab_focus_block=false;
+}
+
+void ScriptEditor::ensure_select_current() {
+
+
+ if (tab_container->get_child_count() && tab_container->get_current_tab()>=0) {
+
+ Node *current = tab_container->get_child(tab_container->get_current_tab());
- Vector<String> open_files=EditorSettings::get_singleton()->get("rpath");
- for(int i=0;i<open_files.size();i++) {
- Ref<Script> scr = ResourceLoader::load(open_files[i]);
- if (!scr.is_valid())
- continue;
- editor->edit_resource(scr);
+ ScriptTextEditor *ste = current->cast_to<ScriptTextEditor>();
+ if (ste) {
+
+ Ref<Script> script = ste->get_edited_script();
+
+ if (!grab_focus_block && is_inside_tree())
+ ste->get_text_edit()->grab_focus();
+
+ edit_menu->show();
+ search_menu->show();
+ script_search_menu->hide();
+
+
+ }
+
+ EditorHelp *eh = current->cast_to<EditorHelp>();
+
+ if (eh) {
+ edit_menu->hide();
+ search_menu->hide();
+ script_search_menu->show();
+
}
}
-}
-void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
- for(int i=0;i<tab_container->get_child_count();i++) {
- ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>();
- if (!ste)
- continue;
- List<int> bpoints;
- ste->get_text_edit()->get_breakpoints(&bpoints);
+}
- Ref<Script> script = ste->get_edited_script();
- String base = script->get_path();
- ERR_CONTINUE( base.begins_with("local://") || base=="" );
+void ScriptEditor::_find_scripts(Node* p_base, Node* p_current, Set<Ref<Script> > &used) {
+ if (p_current!=p_base && p_current->get_owner()!=p_base)
+ return;
- for(List<int>::Element *E=bpoints.front();E;E=E->next()) {
+ if (p_current->get_script_instance()) {
+ Ref<Script> scr = p_current->get_script();
+ if (scr.is_valid())
+ used.insert(scr);
+ }
- p_breakpoints->push_back(base+":"+itos(E->get()+1));
- }
+ for(int i=0;i<p_current->get_child_count();i++) {
+ _find_scripts(p_base,p_current->get_child(i),used);
}
}
+struct _ScriptEditorItemData {
+ String name;
+ Ref<Texture> icon;
+ int index;
+ String tooltip;
+ bool used;
+ int category;
-void ScriptEditor::_bind_methods() {
- ObjectTypeDB::bind_method("_tab_changed",&ScriptEditor::_tab_changed);
- ObjectTypeDB::bind_method("_menu_option",&ScriptEditor::_menu_option);
- ObjectTypeDB::bind_method("_close_current_tab",&ScriptEditor::_close_current_tab);
- ObjectTypeDB::bind_method("_editor_play",&ScriptEditor::_editor_play);
- ObjectTypeDB::bind_method("_editor_pause",&ScriptEditor::_editor_pause);
- ObjectTypeDB::bind_method("_editor_stop",&ScriptEditor::_editor_stop);
- ObjectTypeDB::bind_method("_add_callback",&ScriptEditor::_add_callback);
- ObjectTypeDB::bind_method("_reload_scripts",&ScriptEditor::_reload_scripts);
- ObjectTypeDB::bind_method("_resave_scripts",&ScriptEditor::_resave_scripts);
- ObjectTypeDB::bind_method("_res_saved_callback",&ScriptEditor::_res_saved_callback);
- ObjectTypeDB::bind_method("_goto_script_line",&ScriptEditor::_goto_script_line);
- ObjectTypeDB::bind_method("_goto_script_line2",&ScriptEditor::_goto_script_line2);
- ObjectTypeDB::bind_method("_breaked",&ScriptEditor::_breaked);
- ObjectTypeDB::bind_method("_show_debugger",&ScriptEditor::_show_debugger);
- ObjectTypeDB::bind_method("_get_debug_tooltip",&ScriptEditor::_get_debug_tooltip);
+ bool operator<(const _ScriptEditorItemData& id) const {
-}
+ return category==id.category?name.nocasecmp_to(id.name)<0:category<id.category;
+ }
+};
-void ScriptEditor::ensure_focus_current() {
- int cidx = tab_container->get_current_tab();
- if (cidx<0 || cidx>=tab_container->get_tab_count());
- Control *c = tab_container->get_child(cidx)->cast_to<Control>();
- if (!c)
- return;
- ScriptTextEditor *ste = c->cast_to<ScriptTextEditor>();
- if (!ste)
+void ScriptEditor::_update_script_colors() {
+
+ bool enabled = EditorSettings::get_singleton()->get("text_editor/script_temperature_enabled");
+ if (!enabled)
return;
- ste->get_text_edit()->grab_focus();
+
+ int hist_size = EditorSettings::get_singleton()->get("text_editor/script_temperature_history_size");
+ Color hot_color=EditorSettings::get_singleton()->get("text_editor/script_temperature_hot_color");
+ Color cold_color=EditorSettings::get_singleton()->get("text_editor/script_temperature_cold_color");
+
+ for(int i=0;i<script_list->get_item_count();i++) {
+
+ int c = script_list->get_item_metadata(i);
+ Node *n = tab_container->get_child(c);
+ if (!n)
+ continue;
+
+ script_list->set_item_custom_bg_color(i,Color(0,0,0,0));
+ if (!n->has_meta("__editor_pass")) {
+ continue;
+ }
+
+ int pass=n->get_meta("__editor_pass");
+ int h = edit_pass - pass;
+ if (h>hist_size) {
+ continue;
+ }
+ float v = Math::ease((edit_pass-pass)/float_t(hist_size),0.4);
+
+
+ script_list->set_item_custom_bg_color(i,hot_color.linear_interpolate(cold_color,v));
+ }
}
-void ScriptEditor::ensure_select_current() {
+void ScriptEditor::_update_script_names() {
+ waiting_update_names=false;
+ Set<Ref<Script> > used;
+ Node* edited = EditorNode::get_singleton()->get_edited_scene();
+ if (edited) {
+ _find_scripts(edited,edited,used);
+ }
- if (tab_container->get_child_count() && tab_container->get_current_tab()>=0) {
+ script_list->clear();
+ bool split_script_help = EditorSettings::get_singleton()->get("text_editor/group_help_pages");
- ScriptTextEditor *ste = tab_container->get_child(tab_container->get_current_tab())->cast_to<ScriptTextEditor>();
- if (!ste)
- return;
- Ref<Script> script = ste->get_edited_script();
+ Vector<_ScriptEditorItemData> sedata;
+
+ for(int i=0;i<tab_container->get_child_count();i++) {
+
+
+ ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>();
+ if (ste) {
+
+ String name = ste->get_name();
+ Ref<Texture> icon = ste->get_icon();
+ String tooltip = ste->get_edited_script()->get_path();
+
+ _ScriptEditorItemData sd;
+ sd.icon=icon;
+ sd.name=name;
+ sd.tooltip=tooltip;
+ sd.index=i;
+ sd.used=used.has(ste->get_edited_script());
+ sd.category=0;
+
+ sedata.push_back(sd);
+ }
+
+ EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>();
+ if (eh) {
+
+ String name = eh->get_class_name();
+ Ref<Texture> icon = get_icon("Help","EditorIcons");
+ String tooltip = name+" Class Reference";
+
+ _ScriptEditorItemData sd;
+ sd.icon=icon;
+ sd.name=name;
+ sd.tooltip=tooltip;
+ sd.index=i;
+ sd.used=false;
+ sd.category=split_script_help?1:0;
+ sedata.push_back(sd);
+
+ }
- ste->get_text_edit()->grab_focus();
}
+
+ sedata.sort();
+
+ for(int i=0;i<sedata.size();i++) {
+
+ script_list->add_item(sedata[i].name,sedata[i].icon);
+ int index = script_list->get_item_count()-1;
+ script_list->set_item_tooltip(index,sedata[i].tooltip);
+ script_list->set_item_metadata(index,sedata[i].index);
+ if (sedata[i].used) {
+
+ script_list->set_item_custom_bg_color(index,Color(88/255.0,88/255.0,60/255.0));
+ }
+ if (tab_container->get_current_tab()==sedata[i].index) {
+ script_list->select(index);
+ script_name_label->set_text(sedata[i].name);
+ script_icon->set_texture(sedata[i].icon);
+
+ }
+ }
+
+ _update_script_colors();
+
+
+
+
}
void ScriptEditor::edit(const Ref<Script>& p_script) {
@@ -1368,6 +1788,8 @@ void ScriptEditor::edit(const Ref<Script>& p_script) {
// see if already has it
+ bool open_dominant = EditorSettings::get_singleton()->get("text_editor/open_dominant_script_on_scene_change");
+
if (p_script->get_path().is_resource_file() && bool(EditorSettings::get_singleton()->get("external_editor/use_external_editor"))) {
String path = EditorSettings::get_singleton()->get("external_editor/exec_path");
@@ -1396,9 +1818,14 @@ void ScriptEditor::edit(const Ref<Script>& p_script) {
if (ste->get_edited_script()==p_script) {
- if (tab_container->get_current_tab()!=i)
- tab_container->set_current_tab(i);
- ste->get_text_edit()->grab_focus();
+ if (open_dominant || !EditorNode::get_singleton()->is_changing_scene()) {
+ if (tab_container->get_current_tab()!=i) {
+ _go_to_tab(i);
+ script_list->select( script_list->find_metadata(i) );
+ }
+ if (is_visible())
+ ste->get_text_edit()->grab_focus();
+ }
return;
}
}
@@ -1409,11 +1836,16 @@ void ScriptEditor::edit(const Ref<Script>& p_script) {
ste->set_edited_script(p_script);
ste->get_text_edit()->set_tooltip_request_func(this,"_get_debug_tooltip",ste);
tab_container->add_child(ste);
- tab_container->set_current_tab(tab_container->get_tab_count()-1);
+ _go_to_tab(tab_container->get_tab_count()-1);
- _update_window_menu();
- _save_files_state();
+
+
+ _update_script_names();
+ ste->connect("name_changed",this,"_update_script_names");
+ if (!restoring_layout) {
+ EditorNode::get_singleton()->save_layout();
+ }
}
void ScriptEditor::save_external_data() {
@@ -1473,51 +1905,6 @@ void ScriptEditor::_editor_stop() {
debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true );
}
-void ScriptEditor::_update_window_menu() {
-
- int idx=0;
- for(int i=0;i<tab_container->get_child_count();i++) {
-
- ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>();
- if (!ste)
- continue;
- idx++;
- }
-
- if (idx==0) {
- window_menu->set_disabled(true);
- edit_menu->set_disabled(true);
- search_menu->set_disabled(true);
- return;
- } else {
-
- window_menu->set_disabled(false);
- edit_menu->set_disabled(false);
- search_menu->set_disabled(false);
- }
-
- window_menu->get_popup()->clear();
- window_menu->get_popup()->add_item("Close",WINDOW_CLOSE,KEY_MASK_CMD|KEY_W);
- window_menu->get_popup()->add_separator();
- window_menu->get_popup()->add_item("Move Left",WINDOW_MOVE_LEFT,KEY_MASK_CMD|KEY_LEFT);
- window_menu->get_popup()->add_item("Move Right",WINDOW_MOVE_RIGHT,KEY_MASK_CMD|KEY_RIGHT);
- window_menu->get_popup()->add_separator();
-
- idx=0;
- for(int i=0;i<tab_container->get_child_count();i++) {
-
- ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>();
- if (!ste)
- continue;
- String n = ste->get_name();
- uint32_t accel=0;
- if (idx<9) {
- accel=KEY_MASK_ALT|(KEY_1+idx);
- }
- window_menu->get_popup()->add_item(n,WINDOW_SELECT_BASE+idx,accel);
- idx++;
- }
-}
void ScriptEditor::_add_callback(Object *p_obj, const String& p_function, const StringArray& p_args) {
@@ -1549,18 +1936,284 @@ void ScriptEditor::_add_callback(Object *p_obj, const String& p_function, const
ste->get_text_edit()->insert_text_at_cursor("\n\n"+func);
}
- tab_container->set_current_tab(i);
+ _go_to_tab(i);
ste->get_text_edit()->cursor_set_line(pos);
ste->get_text_edit()->cursor_set_column(1);
+ script_list->select( script_list->find_metadata(i) );
+
break;
}
}
+void ScriptEditor::_editor_settings_changed() {
+
+ print_line("settings changed");
+ float autosave_time = EditorSettings::get_singleton()->get("text_editor/autosave_interval_secs");
+ if (autosave_time>0) {
+ autosave_timer->set_wait_time(autosave_time);
+ autosave_timer->start();
+ } else {
+ autosave_timer->stop();
+ }
+
+}
+
+void ScriptEditor::_autosave_scripts() {
+
+ print_line("autosaving");
+ save_external_data();
+}
+
+void ScriptEditor::_tree_changed() {
+
+ if (waiting_update_names)
+ return;
+
+ waiting_update_names=true;
+ call_deferred("_update_script_names");
+}
+
+void ScriptEditor::_script_split_dragged(float) {
+
+ EditorNode::get_singleton()->save_layout();
+}
+
+void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
+
+ if (!bool(EDITOR_DEF("text_editor/restore_scripts_on_load",true))) {
+ return;
+ }
+
+ if (!p_layout->has_section_key("ScriptEditor","open_scripts") && !p_layout->has_section_key("ScriptEditor","open_help"))
+ return;
+
+ Array scripts = p_layout->get_value("ScriptEditor","open_scripts");
+ Array helps;
+ if (p_layout->has_section_key("ScriptEditor","open_help"))
+ helps=p_layout->get_value("ScriptEditor","open_help");
+
+ restoring_layout=true;
+
+ for(int i=0;i<scripts.size();i++) {
+
+ String path = scripts[i];
+ Ref<Script> scr = ResourceLoader::load(path);
+ if (scr.is_valid()) {
+ edit(scr);
+ }
+ }
+
+
+ for(int i=0;i<helps.size();i++) {
+
+ String path = helps[i];
+ _help_class_open(path);
+ }
+
+ for(int i=0;i<tab_container->get_child_count();i++) {
+ tab_container->get_child(i)->set_meta("__editor_pass",Variant());
+ }
+
+
+ if (p_layout->has_section_key("ScriptEditor","split_offset")) {
+ script_split->set_split_offset(p_layout->get_value("ScriptEditor","split_offset"));
+ }
+
+
+ restoring_layout=false;
+
+}
+
+void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
+
+ Array scripts;
+ Array helps;
+
+ for(int i=0;i<tab_container->get_child_count();i++) {
+
+ ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>();
+ if (ste) {
+
+ String path = ste->get_edited_script()->get_path();
+ if (!path.is_resource_file())
+ continue;
+
+ scripts.push_back(path);
+ }
+
+ EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>();
+
+ if (eh) {
+
+ helps.push_back(eh->get_class_name());
+ }
+
+
+ }
+
+ p_layout->set_value("ScriptEditor","open_scripts",scripts);
+ p_layout->set_value("ScriptEditor","open_help",helps);
+ p_layout->set_value("ScriptEditor","split_offset",script_split->get_split_offset());
+
+}
+
+
+void ScriptEditor::_help_class_open(const String& p_class) {
+
+
+ for(int i=0;i<tab_container->get_child_count();i++) {
+
+ EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>();
+
+ if (eh && eh->get_class_name()==p_class) {
+
+ _go_to_tab(i);
+ _update_script_names();
+ return;
+ }
+ }
+
+ EditorHelp * eh = memnew( EditorHelp );
+
+
+ eh->set_name(p_class);
+ tab_container->add_child(eh);
+ _go_to_tab(tab_container->get_tab_count()-1);
+ eh->go_to_class(p_class,0);
+ eh->connect("go_to_help",this,"_help_class_goto");
+ _update_script_names();
+
+}
+
+void ScriptEditor::_help_class_goto(const String& p_desc) {
+
+
+ String cname=p_desc.get_slice(":",1);
+
+ for(int i=0;i<tab_container->get_child_count();i++) {
+
+ EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>();
+
+ if (eh && eh->get_class_name()==cname) {
+
+ _go_to_tab(i);
+ eh->go_to_help(p_desc);
+ _update_script_names();
+ return;
+ }
+ }
+
+ EditorHelp * eh = memnew( EditorHelp );
+
+ eh->set_name(cname);
+ tab_container->add_child(eh);
+ _go_to_tab(tab_container->get_tab_count()-1);
+ eh->go_to_help(p_desc);
+ eh->connect("go_to_help",this,"_help_class_goto");
+ _update_script_names();
+
+}
+
+void ScriptEditor::_update_history_pos(int p_new_pos) {
+
+ Node *n = tab_container->get_current_tab_control();
+
+ if (n->cast_to<ScriptTextEditor>()) {
+
+ history[history_pos].scroll_pos=n->cast_to<ScriptTextEditor>()->get_text_edit()->get_v_scroll();
+ history[history_pos].cursor_column=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_column();
+ history[history_pos].cursor_row=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_line();
+ }
+ if (n->cast_to<EditorHelp>()) {
+
+ history[history_pos].scroll_pos=n->cast_to<EditorHelp>()->get_scroll();
+ }
+
+ history_pos=p_new_pos;
+ tab_container->set_current_tab(history[history_pos].control->get_index());
+
+ n = history[history_pos].control;
+
+ if (n->cast_to<ScriptTextEditor>()) {
+
+ n->cast_to<ScriptTextEditor>()->get_text_edit()->set_v_scroll(history[history_pos].scroll_pos);
+ n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_set_column( history[history_pos].cursor_column );
+ n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_set_line( history[history_pos].cursor_row );
+ n->cast_to<ScriptTextEditor>()->get_text_edit()->grab_focus();
+ }
+
+ if (n->cast_to<EditorHelp>()) {
+
+ n->cast_to<EditorHelp>()->set_scroll(history[history_pos].scroll_pos);
+ n->cast_to<EditorHelp>()->set_focused();
+ }
+
+ n->set_meta("__editor_pass",++edit_pass);
+ _update_script_names();
+ _update_history_arrows();
+
+}
+
+void ScriptEditor::_history_forward() {
+
+ if (history_pos<history.size()-1) {
+ _update_history_pos(history_pos+1);
+ }
+}
+
+void ScriptEditor::_history_back(){
+
+ if (history_pos>0) {
+ _update_history_pos(history_pos-1);
+ }
+
+}
+void ScriptEditor::set_scene_root_script( Ref<Script> p_script ) {
+
+ bool open_dominant = EditorSettings::get_singleton()->get("text_editor/open_dominant_script_on_scene_change");
+ if (open_dominant && p_script.is_valid()) {
+ edit(p_script);
+ }
+}
+
+void ScriptEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_tab_changed",&ScriptEditor::_tab_changed);
+ ObjectTypeDB::bind_method("_menu_option",&ScriptEditor::_menu_option);
+ ObjectTypeDB::bind_method("_close_current_tab",&ScriptEditor::_close_current_tab);
+ ObjectTypeDB::bind_method("_editor_play",&ScriptEditor::_editor_play);
+ ObjectTypeDB::bind_method("_editor_pause",&ScriptEditor::_editor_pause);
+ ObjectTypeDB::bind_method("_editor_stop",&ScriptEditor::_editor_stop);
+ ObjectTypeDB::bind_method("_add_callback",&ScriptEditor::_add_callback);
+ ObjectTypeDB::bind_method("_reload_scripts",&ScriptEditor::_reload_scripts);
+ ObjectTypeDB::bind_method("_resave_scripts",&ScriptEditor::_resave_scripts);
+ ObjectTypeDB::bind_method("_res_saved_callback",&ScriptEditor::_res_saved_callback);
+ ObjectTypeDB::bind_method("_goto_script_line",&ScriptEditor::_goto_script_line);
+ ObjectTypeDB::bind_method("_goto_script_line2",&ScriptEditor::_goto_script_line2);
+ ObjectTypeDB::bind_method("_breaked",&ScriptEditor::_breaked);
+ ObjectTypeDB::bind_method("_show_debugger",&ScriptEditor::_show_debugger);
+ ObjectTypeDB::bind_method("_get_debug_tooltip",&ScriptEditor::_get_debug_tooltip);
+ ObjectTypeDB::bind_method("_autosave_scripts",&ScriptEditor::_autosave_scripts);
+ ObjectTypeDB::bind_method("_editor_settings_changed",&ScriptEditor::_editor_settings_changed);
+ ObjectTypeDB::bind_method("_update_script_names",&ScriptEditor::_update_script_names);
+ ObjectTypeDB::bind_method("_tree_changed",&ScriptEditor::_tree_changed);
+ ObjectTypeDB::bind_method("_script_selected",&ScriptEditor::_script_selected);
+ ObjectTypeDB::bind_method("_script_created",&ScriptEditor::_script_created);
+ ObjectTypeDB::bind_method("_script_split_dragged",&ScriptEditor::_script_split_dragged);
+ ObjectTypeDB::bind_method("_help_class_open",&ScriptEditor::_help_class_open);
+ ObjectTypeDB::bind_method("_help_class_goto",&ScriptEditor::_help_class_goto);
+ ObjectTypeDB::bind_method("_history_forward",&ScriptEditor::_history_forward);
+ ObjectTypeDB::bind_method("_history_back",&ScriptEditor::_history_back);
+}
+
ScriptEditor::ScriptEditor(EditorNode *p_editor) {
+ completion_cache = memnew( EditorScriptCodeCompletionCache );
+ restoring_layout=false;
+ waiting_update_names=false;
editor=p_editor;
menu_hb = memnew( HBoxContainer );
@@ -1570,17 +2223,36 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
add_child(v_split);
v_split->set_v_size_flags(SIZE_EXPAND_FILL);
+ script_split = memnew( HSplitContainer );
+ v_split->add_child(script_split);
+ script_split->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ script_list = memnew( ItemList );
+ script_split->add_child(script_list);
+ script_list->set_custom_minimum_size(Size2(70,0));
+ script_split->set_split_offset(70);
+
tab_container = memnew( TabContainer );
- v_split->add_child(tab_container);
- tab_container->set_v_size_flags(SIZE_EXPAND_FILL);
+ tab_container->set_tabs_visible(false);
+ script_split->add_child(tab_container);
+
+
+ tab_container->set_h_size_flags(SIZE_EXPAND_FILL);
file_menu = memnew( MenuButton );
menu_hb->add_child(file_menu);
file_menu->set_text("File");
+ file_menu->get_popup()->add_item("New",FILE_NEW);
file_menu->get_popup()->add_item("Open",FILE_OPEN);
- file_menu->get_popup()->add_item("Save",FILE_SAVE,KEY_MASK_ALT|KEY_S);
+ file_menu->get_popup()->add_separator();
+ file_menu->get_popup()->add_item("Save",FILE_SAVE,KEY_MASK_ALT|KEY_MASK_CMD|KEY_S);
file_menu->get_popup()->add_item("Save As..",FILE_SAVE_AS);
file_menu->get_popup()->add_item("Save All",FILE_SAVE_ALL,KEY_MASK_CMD|KEY_MASK_SHIFT|KEY_S);
+ file_menu->get_popup()->add_separator();
+ file_menu->get_popup()->add_item("History Prev",WINDOW_PREV,KEY_MASK_CTRL|KEY_MASK_ALT|KEY_LEFT);
+ file_menu->get_popup()->add_item("History Next",WINDOW_NEXT,KEY_MASK_CTRL|KEY_MASK_ALT|KEY_RIGHT);
+ file_menu->get_popup()->add_separator();
+ file_menu->get_popup()->add_item("Close",FILE_CLOSE,KEY_MASK_CMD|KEY_W);
file_menu->get_popup()->connect("item_pressed", this,"_menu_option");
edit_menu = memnew( MenuButton );
@@ -1602,7 +2274,11 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
edit_menu->get_popup()->add_item("Toggle Comment",EDIT_TOGGLE_COMMENT,KEY_MASK_CMD|KEY_K);
edit_menu->get_popup()->add_item("Clone Down",EDIT_CLONE_DOWN,KEY_MASK_CMD|KEY_B);
edit_menu->get_popup()->add_separator();
+#ifdef OSX_ENABLED
+ edit_menu->get_popup()->add_item("Complete Symbol",EDIT_COMPLETE,KEY_MASK_CTRL|KEY_SPACE);
+#else
edit_menu->get_popup()->add_item("Complete Symbol",EDIT_COMPLETE,KEY_MASK_CMD|KEY_SPACE);
+#endif
edit_menu->get_popup()->add_item("Auto Indent",EDIT_AUTO_INDENT,KEY_MASK_CMD|KEY_I);
edit_menu->get_popup()->connect("item_pressed", this,"_menu_option");
@@ -1611,13 +2287,22 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
menu_hb->add_child(search_menu);
search_menu->set_text("Search");
search_menu->get_popup()->add_item("Find..",SEARCH_FIND,KEY_MASK_CMD|KEY_F);
- search_menu->get_popup()->add_item("Find Next",SEARCH_FIND_NEXT,KEY_MASK_CMD|KEY_G);
+ search_menu->get_popup()->add_item("Find Next",SEARCH_FIND_NEXT,KEY_F3);
search_menu->get_popup()->add_item("Replace..",SEARCH_REPLACE,KEY_MASK_CMD|KEY_R);
search_menu->get_popup()->add_separator();
search_menu->get_popup()->add_item("Goto Function..",SEARCH_LOCATE_FUNCTION,KEY_MASK_SHIFT|KEY_MASK_CMD|KEY_F);
search_menu->get_popup()->add_item("Goto Line..",SEARCH_GOTO_LINE,KEY_MASK_CMD|KEY_L);
search_menu->get_popup()->connect("item_pressed", this,"_menu_option");
+ script_search_menu = memnew( MenuButton );
+ menu_hb->add_child(script_search_menu);
+ script_search_menu->set_text("Search");
+ script_search_menu->get_popup()->add_item("Find..",SEARCH_FIND,KEY_MASK_CMD|KEY_F);
+ script_search_menu->get_popup()->add_item("Find Next",SEARCH_FIND_NEXT,KEY_F3);
+ script_search_menu->get_popup()->connect("item_pressed", this,"_menu_option");
+ script_search_menu->hide();
+
+
debug_menu = memnew( MenuButton );
menu_hb->add_child(debug_menu);
debug_menu->set_text("Debug");
@@ -1638,6 +2323,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true );
+#if 0
window_menu = memnew( MenuButton );
menu_hb->add_child(window_menu);
window_menu->set_text("Window");
@@ -1648,12 +2334,61 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
window_menu->get_popup()->add_separator();
window_menu->get_popup()->connect("item_pressed", this,"_menu_option");
+#endif
+
help_menu = memnew( MenuButton );
menu_hb->add_child(help_menu);
help_menu->set_text("Help");
help_menu->get_popup()->add_item("Contextual", HELP_CONTEXTUAL, KEY_MASK_SHIFT|KEY_F1);
help_menu->get_popup()->connect("item_pressed", this,"_menu_option");
+ menu_hb->add_spacer();
+
+
+ script_icon = memnew( TextureFrame );
+ menu_hb->add_child(script_icon);
+ script_name_label = memnew( Label );
+ menu_hb->add_child(script_name_label);
+
+ script_icon->hide();
+ script_name_label->hide();
+
+ menu_hb->add_spacer();
+
+ site_search = memnew( ToolButton );
+ site_search->set_text("Tutorials");
+ site_search->connect("pressed",this,"_menu_option",varray(SEARCH_WEBSITE));
+ menu_hb->add_child(site_search);
+ site_search->set_tooltip("Open http://www.godotengine.org at tutorials section.");
+
+ class_search = memnew( ToolButton );
+ class_search->set_text("Classes");
+ class_search->connect("pressed",this,"_menu_option",varray(SEARCH_CLASSES));
+ menu_hb->add_child(class_search);
+ class_search->set_tooltip("Search the class hierarchy.");
+
+ help_search = memnew( ToolButton );
+ help_search->set_text("Search Help");
+ help_search->connect("pressed",this,"_menu_option",varray(SEARCH_HELP));
+ menu_hb->add_child(help_search);
+ help_search->set_tooltip("Search the reference documentation.");
+
+ menu_hb->add_child( memnew( VSeparator) );
+
+ script_back = memnew( ToolButton );
+ script_back->connect("pressed",this,"_history_back");
+ menu_hb->add_child(script_back);
+ script_back->set_disabled(true);
+ help_search->set_tooltip("Go to previous edited document.");
+
+ script_forward = memnew( ToolButton );
+ script_forward->connect("pressed",this,"_history_forward");
+ menu_hb->add_child(script_forward);
+ script_forward->set_disabled(true);
+ help_search->set_tooltip("Go to next edited document.");
+
+
+
tab_container->connect("tab_changed", this,"_tab_changed");
find_replace_dialog = memnew(FindReplaceDialog);
@@ -1663,6 +2398,10 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
add_child(erase_tab_confirm);
erase_tab_confirm->connect("confirmed", this,"_close_current_tab");
+ script_create_dialog = memnew(ScriptCreateDialog);
+ script_create_dialog->set_title("Create Script");
+ add_child(script_create_dialog);
+ script_create_dialog->connect("script_created", this, "_script_created");
goto_line_dialog = memnew(GotoLineDialog);
add_child(goto_line_dialog);
@@ -1705,11 +2444,35 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
v_split->add_child(debugger);
debugger->connect("breaked",this,"_breaked");
+
+ autosave_timer = memnew( Timer );
+ autosave_timer->set_one_shot(false);
+ add_child(autosave_timer);
+
+ grab_focus_block=false;
+
+ help_search_dialog = memnew( EditorHelpSearch );
+ add_child(help_search_dialog);
+ help_search_dialog->connect("go_to_help",this,"_help_class_goto");
+
+
+ help_index = memnew( EditorHelpIndex );
+ add_child(help_index);
+ help_index->connect("open_class",this,"_help_class_open");
+
+ history_pos=-1;
// debugger_gui->hide();
+ edit_pass=0;
+
}
+ScriptEditor::~ScriptEditor() {
+
+ memdelete(completion_cache);
+}
+
void ScriptEditorPlugin::edit(Object *p_object) {
if (!p_object->cast_to<Script>())
@@ -1769,20 +2532,24 @@ void ScriptEditorPlugin::apply_changes() {
void ScriptEditorPlugin::restore_global_state() {
- if (bool(EDITOR_DEF("text_editor/restore_scripts_on_load",true))) {
- script_editor->_load_files_state();
- }
}
void ScriptEditorPlugin::save_global_state() {
- if (bool(EDITOR_DEF("text_editor/restore_scripts_on_load",true))) {
- script_editor->_save_files_state();
- }
+}
+
+void ScriptEditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
+ script_editor->set_window_layout(p_layout);
}
+void ScriptEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout){
+
+ script_editor->get_window_layout(p_layout);
+}
+
+
void ScriptEditorPlugin::get_breakpoints(List<String> *p_breakpoints) {
@@ -1798,8 +2565,15 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
script_editor->hide();
+ EDITOR_DEF("text_editor/auto_reload_changed_scripts",false);
+ EDITOR_DEF("text_editor/open_dominant_script_on_scene_change",true);
EDITOR_DEF("external_editor/use_external_editor",false);
EDITOR_DEF("external_editor/exec_path","");
+ EDITOR_DEF("text_editor/script_temperature_enabled",true);
+ EDITOR_DEF("text_editor/script_temperature_history_size",15);
+ EDITOR_DEF("text_editor/script_temperature_hot_color",Color(1,0,0,0.3));
+ EDITOR_DEF("text_editor/script_temperature_cold_color",Color(0,0,1,0.3));
+ EDITOR_DEF("text_editor/group_help_pages",false);
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"external_editor/exec_path",PROPERTY_HINT_GLOBAL_FILE));
EDITOR_DEF("external_editor/exec_flags","");
diff --git a/tools/editor/plugins/script_editor_plugin.h b/tools/editor/plugins/script_editor_plugin.h
index 136d966587..e755f570ef 100644
--- a/tools/editor/plugins/script_editor_plugin.h
+++ b/tools/editor/plugins/script_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,15 +30,18 @@
#define SCRIPT_EDITOR_PLUGIN_H
#include "tools/editor/editor_plugin.h"
+#include "tools/editor/script_create_dialog.h"
#include "scene/gui/tab_container.h"
#include "scene/gui/text_edit.h"
#include "scene/gui/menu_button.h"
+#include "scene/gui/tool_button.h"
#include "scene/gui/tree.h"
#include "scene/main/timer.h"
#include "script_language.h"
#include "tools/editor/code_editor.h"
#include "scene/gui/split_container.h"
-
+#include "scene/gui/item_list.h"
+#include "tools/editor/editor_help.h"
class ScriptEditorQuickOpen : public ConfirmationDialog {
@@ -88,6 +91,7 @@ protected:
virtual void _code_complete_script(const String& p_code, List<String>* r_options);
virtual void _load_theme_settings();
void _notification(int p_what);
+ static void _bind_methods();
public:
@@ -97,12 +101,15 @@ public:
Vector<String> get_functions() ;
void set_edited_script(const Ref<Script>& p_script);
void reload_text();
- void _update_name();
+ String get_name() ;
+ Ref<Texture> get_icon() ;
ScriptTextEditor();
};
+class EditorScriptCodeCompletionCache;
+
class ScriptEditor : public VBoxContainer {
OBJ_TYPE(ScriptEditor, VBoxContainer );
@@ -110,11 +117,12 @@ class ScriptEditor : public VBoxContainer {
EditorNode *editor;
enum {
-
+ FILE_NEW,
FILE_OPEN,
FILE_SAVE,
FILE_SAVE_AS,
FILE_SAVE_ALL,
+ FILE_CLOSE,
EDIT_UNDO,
EDIT_REDO,
EDIT_CUT,
@@ -123,27 +131,31 @@ class ScriptEditor : public VBoxContainer {
EDIT_SELECT_ALL,
EDIT_COMPLETE,
EDIT_AUTO_INDENT,
- EDIT_TOGGLE_COMMENT,
- EDIT_MOVE_LINE_UP,
- EDIT_MOVE_LINE_DOWN,
- EDIT_INDENT_RIGHT,
- EDIT_INDENT_LEFT,
- EDIT_CLONE_DOWN,
+ EDIT_TOGGLE_COMMENT,
+ EDIT_MOVE_LINE_UP,
+ EDIT_MOVE_LINE_DOWN,
+ EDIT_INDENT_RIGHT,
+ EDIT_INDENT_LEFT,
+ EDIT_CLONE_DOWN,
SEARCH_FIND,
SEARCH_FIND_NEXT,
SEARCH_REPLACE,
SEARCH_LOCATE_FUNCTION,
SEARCH_GOTO_LINE,
+ SEARCH_HELP,
+ SEARCH_CLASSES,
+ SEARCH_WEBSITE,
DEBUG_TOGGLE_BREAKPOINT,
DEBUG_NEXT,
DEBUG_STEP,
DEBUG_BREAK,
DEBUG_CONTINUE,
DEBUG_SHOW,
- HELP_CONTEXTUAL,
- WINDOW_CLOSE,
+ HELP_CONTEXTUAL,
WINDOW_MOVE_LEFT,
WINDOW_MOVE_RIGHT,
+ WINDOW_NEXT,
+ WINDOW_PREV,
WINDOW_SELECT_BASE=100
};
@@ -151,16 +163,47 @@ class ScriptEditor : public VBoxContainer {
MenuButton *file_menu;
MenuButton *edit_menu;
MenuButton *search_menu;
- MenuButton *window_menu;
+ MenuButton *script_search_menu;
MenuButton *debug_menu;
MenuButton *help_menu;
+ Timer *autosave_timer;
uint64_t idle;
+ Button *help_search;
+ Button *site_search;
+ Button *class_search;
+ EditorHelpSearch *help_search_dialog;
+
+ ItemList *script_list;
+ HSplitContainer *script_split;
TabContainer *tab_container;
FindReplaceDialog *find_replace_dialog;
GotoLineDialog *goto_line_dialog;
ConfirmationDialog *erase_tab_confirm;
+ ScriptCreateDialog *script_create_dialog;
ScriptEditorDebugger* debugger;
+ ToolButton *scripts_visible;
+
+ TextureFrame *script_icon;
+ Label *script_name_label;
+
+ ToolButton *script_back;
+ ToolButton *script_forward;
+
+
+ struct ScriptHistory {
+
+ Control *control;
+ int scroll_pos;
+ int cursor_column;
+ int cursor_row;
+ };
+
+ Vector<ScriptHistory> history;
+ int history_pos;
+
+
+ EditorHelpIndex *help_index;
void _tab_changed(int p_which);
void _menu_option(int p_optin);
@@ -170,6 +213,8 @@ class ScriptEditor : public VBoxContainer {
VSplitContainer *v_split;
+ bool restoring_layout;
+
String _get_debug_tooltip(const String&p_text,Node *_ste);
void _resave_scripts(const String& p_str);
@@ -179,13 +224,18 @@ class ScriptEditor : public VBoxContainer {
void _close_current_tab();
+ bool grab_focus_block;
+
ScriptEditorQuickOpen *quick_open;
+ EditorScriptCodeCompletionCache *completion_cache;
void _editor_play();
void _editor_pause();
void _editor_stop();
+ int edit_pass;
+
void _add_callback(Object *p_obj, const String& p_function, const StringArray& p_args);
void _res_saved_callback(const Ref<Resource>& p_res);
@@ -194,6 +244,34 @@ class ScriptEditor : public VBoxContainer {
void _breaked(bool p_breaked,bool p_can_debug);
void _show_debugger(bool p_show);
void _update_window_menu();
+ void _script_created(Ref<Script> p_script);
+
+ void _editor_settings_changed();
+ void _autosave_scripts();
+
+ void _update_script_names();
+
+ void _script_selected(int p_idx);
+
+ void _find_scripts(Node* p_base, Node* p_current,Set<Ref<Script> >& used);
+
+ void _tree_changed();
+
+ void _script_split_dragged(float);
+
+
+ void _history_forward();
+ void _history_back();
+
+ bool waiting_update_names;
+
+ void _help_class_open(const String& p_class);
+ void _help_class_goto(const String& p_desc);
+ void _update_history_arrows();
+ void _go_to_tab(int p_idx);
+ void _update_history_pos(int p_new_pos);
+ void _update_script_colors();
+
static ScriptEditor *script_editor;
protected:
@@ -202,9 +280,6 @@ protected:
public:
static ScriptEditor *get_singleton() { return script_editor; }
- void _save_files_state();
- void _load_files_state();
-
void ensure_focus_current();
void apply_scripts() const;
@@ -218,11 +293,19 @@ public:
void get_breakpoints(List<String> *p_breakpoints);
- void swap_lines(TextEdit *tx, int line1, int line2);
+ void swap_lines(TextEdit *tx, int line1, int line2);
void save_external_data();
+ void set_window_layout(Ref<ConfigFile> p_layout);
+ void get_window_layout(Ref<ConfigFile> p_layout);
+
+ void set_scene_root_script( Ref<Script> p_script );
+
+ ScriptEditorDebugger *get_debugger() { return debugger; }
+
ScriptEditor(EditorNode *p_editor);
+ ~ScriptEditor();
};
class ScriptEditorPlugin : public EditorPlugin {
@@ -250,6 +333,9 @@ public:
virtual void restore_global_state();
virtual void save_global_state();
+ virtual void set_window_layout(Ref<ConfigFile> p_layout);
+ virtual void get_window_layout(Ref<ConfigFile> p_layout);
+
virtual void get_breakpoints(List<String> *p_breakpoints);
diff --git a/tools/editor/plugins/shader_editor_plugin.cpp b/tools/editor/plugins/shader_editor_plugin.cpp
index 3166383fc8..a182d57742 100644
--- a/tools/editor/plugins/shader_editor_plugin.cpp
+++ b/tools/editor/plugins/shader_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -57,9 +57,9 @@ void ShaderTextEditor::set_edited_shader(const Ref<Shader>& p_shader,ShaderLangu
_load_theme_settings();
- if (p_type==ShaderLanguage::SHADER_MATERIAL_LIGHT)
+ if (p_type==ShaderLanguage::SHADER_MATERIAL_LIGHT || p_type==ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT)
get_text_edit()->set_text(shader->get_light_code());
- else if (p_type==ShaderLanguage::SHADER_MATERIAL_VERTEX)
+ else if (p_type==ShaderLanguage::SHADER_MATERIAL_VERTEX || p_type==ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX)
get_text_edit()->set_text(shader->get_vertex_code());
else
get_text_edit()->set_text(shader->get_fragment_code());
@@ -81,6 +81,7 @@ void ShaderTextEditor::_load_theme_settings() {
get_text_edit()->add_color_override("font_selected_color",EDITOR_DEF("text_editor/text_selected_color",Color(1,1,1)));
get_text_edit()->add_color_override("selection_color",EDITOR_DEF("text_editor/selection_color",Color(0.2,0.2,1)));
get_text_edit()->add_color_override("brace_mismatch_color",EDITOR_DEF("text_editor/brace_mismatch_color",Color(1,0.2,0.2)));
+ get_text_edit()->add_color_override("current_line_color",EDITOR_DEF("text_editor/current_line_color",Color(0.3,0.5,0.8,0.15)));
Color keyword_color= EDITOR_DEF("text_editor/keyword_color",Color(0.5,0.0,0.2));
@@ -131,17 +132,12 @@ void ShaderTextEditor::_validate_script() {
String errortxt;
int line,col;
- String code;
- if (type==ShaderLanguage::SHADER_MATERIAL_LIGHT)
- code=get_text_edit()->get_text();
- else if (type==ShaderLanguage::SHADER_MATERIAL_VERTEX)
- code=get_text_edit()->get_text();
- else
- code=get_text_edit()->get_text();
-
+ String code=get_text_edit()->get_text();
//List<StringName> params;
//shader->get_param_list(&params);
+ print_line("compile: type: "+itos(type)+" code:\n"+code);
+
Error err = ShaderLanguage::compile(code,type,NULL,NULL,&errortxt,&line,&col);
if (err!=OK) {
@@ -233,25 +229,7 @@ void ShaderEditor::_menu_option(int p_option) {
goto_line_dialog->popup_find_line(current->get_text_edit());
} break;
- case SHADER_POST_PROCESS_MODE:{
-
- fragment_editor->set_edited_shader(shader,ShaderLanguage::SHADER_POST_PROCESS);
- fragment_editor->_validate_script();
- apply_shaders();
- settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE), false);
- settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_POST_PROCESS_MODE), true);
-
-
- } break;
- case SHADER_MATERIAL_MODE: {
- fragment_editor->set_edited_shader(shader,ShaderLanguage::SHADER_MATERIAL_FRAGMENT);
- fragment_editor->_validate_script();
- apply_shaders();
- settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE), true);
- settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_POST_PROCESS_MODE), false);
-
- } break;
}
}
@@ -408,18 +386,17 @@ void ShaderEditor::edit(const Ref<Shader>& p_shader) {
shader=p_shader;
if (shader->get_mode()==Shader::MODE_MATERIAL) {
+ vertex_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_MATERIAL_VERTEX);
fragment_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_MATERIAL_FRAGMENT);
light_editor->set_edited_shader(shader,ShaderLanguage::SHADER_MATERIAL_LIGHT);
- settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE), true);
- settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_POST_PROCESS_MODE), false);
- } else {
+ } else if (shader->get_mode()==Shader::MODE_CANVAS_ITEM) {
- fragment_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_POST_PROCESS);
- settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE), false);
- settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_POST_PROCESS_MODE), true);
+ vertex_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX);
+ fragment_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT);
+ light_editor->set_edited_shader(shader,ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT);
}
- vertex_editor->set_edited_shader(shader,ShaderLanguage::SHADER_MATERIAL_VERTEX);
+ //vertex_editor->set_edited_shader(shader,ShaderLanguage::SHADER_MATERIAL_VERTEX);
// see if already has it
@@ -495,15 +472,6 @@ ShaderEditor::ShaderEditor() {
search_menu->get_popup()->add_item("Goto Line..",SEARCH_GOTO_LINE,KEY_MASK_CMD|KEY_G);
search_menu->get_popup()->connect("item_pressed", this,"_menu_option");
- settings_menu = memnew( MenuButton );
- add_child(settings_menu);
- settings_menu->set_pos(Point2(90,-1));
- settings_menu->set_text("Shader");
- settings_menu->get_popup()->add_check_item("Material Mode",SHADER_MATERIAL_MODE);
- settings_menu->get_popup()->set_item_checked(settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE),true);
- settings_menu->get_popup()->add_check_item("Post Process Mode",SHADER_POST_PROCESS_MODE);
-
- settings_menu->get_popup()->connect("item_pressed", this,"_menu_option");
tab_container->connect("tab_changed", this,"_tab_changed");
@@ -550,7 +518,13 @@ void ShaderEditorPlugin::edit(Object *p_object) {
bool ShaderEditorPlugin::handles(Object *p_object) const {
- return p_object->is_type("Shader");
+ Shader *shader=p_object->cast_to<Shader>();
+ if (!shader)
+ return false;
+ if (_2d)
+ return shader->get_mode()==Shader::MODE_CANVAS_ITEM;
+ else
+ return shader->get_mode()==Shader::MODE_MATERIAL;
}
void ShaderEditorPlugin::make_visible(bool p_visible) {
@@ -596,12 +570,15 @@ void ShaderEditorPlugin::apply_changes() {
shader_editor->apply_shaders();
}
-ShaderEditorPlugin::ShaderEditorPlugin(EditorNode *p_node) {
+ShaderEditorPlugin::ShaderEditorPlugin(EditorNode *p_node, bool p_2d) {
editor=p_node;
shader_editor = memnew( ShaderEditor );
-
- SpatialEditor::get_singleton()->get_shader_split()->add_child(shader_editor);
+ _2d=p_2d;
+ if (p_2d)
+ add_custom_control(CONTAINER_CANVAS_EDITOR_BOTTOM,shader_editor);
+ else
+ add_custom_control(CONTAINER_SPATIAL_EDITOR_BOTTOM,shader_editor);
// editor->get_viewport()->add_child(shader_editor);
// shader_editor->set_area_as_parent_rect();
diff --git a/tools/editor/plugins/shader_editor_plugin.h b/tools/editor/plugins/shader_editor_plugin.h
index 49caee5da6..4ead2ba94e 100644
--- a/tools/editor/plugins/shader_editor_plugin.h
+++ b/tools/editor/plugins/shader_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -79,9 +79,6 @@ class ShaderEditor : public Control {
SEARCH_REPLACE,
//SEARCH_LOCATE_SYMBOL,
SEARCH_GOTO_LINE,
- SHADER_MATERIAL_MODE,
- SHADER_POST_PROCESS_MODE,
- SHADER_SHADE_MODEL_MODE,
};
@@ -134,6 +131,7 @@ class ShaderEditorPlugin : public EditorPlugin {
OBJ_TYPE( ShaderEditorPlugin, EditorPlugin );
+ bool _2d;
ShaderEditor *shader_editor;
EditorNode *editor;
public:
@@ -152,7 +150,7 @@ public:
virtual void save_external_data();
virtual void apply_changes();
- ShaderEditorPlugin(EditorNode *p_node);
+ ShaderEditorPlugin(EditorNode *p_node,bool p_2d);
~ShaderEditorPlugin();
};
diff --git a/tools/editor/plugins/shader_graph_editor_plugin.cpp b/tools/editor/plugins/shader_graph_editor_plugin.cpp
index 2686ca895e..3a7dc26466 100644
--- a/tools/editor/plugins/shader_graph_editor_plugin.cpp
+++ b/tools/editor/plugins/shader_graph_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,1082 +28,2807 @@
/*************************************************************************/
#include "shader_graph_editor_plugin.h"
-#if 0
+
+#include "scene/gui/check_box.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/panel.h"
+#include "spatial_editor_plugin.h"
+#include "os/keyboard.h"
+#include "canvas_item_editor_plugin.h"
-class _ShaderTester : public ShaderCodeGenerator {
-public:
+void GraphColorRampEdit::_input_event(const InputEvent& p_event) {
- Set<int> *_set;
+ if (p_event.type==InputEvent::KEY && p_event.key.pressed && p_event.key.scancode==KEY_DELETE && grabbed!=-1) {
- virtual void begin() {}
- virtual Error add_node(VS::ShaderNodeType p_type,int p_node_pos,int p_id,const Variant& p_param,const Vector<int>& p_in_connections,const Vector<int>& p_out_connections,const Vector<int>& p_out_connection_outputs) { if (_set) _set->insert(p_id); return OK; }
- virtual void end() {}
+ points.remove(grabbed);
+ grabbed=-1;
+ update();
+ emit_signal("ramp_changed");
+ accept_event();
+ }
- _ShaderTester() { _set=NULL; }
-};
+ if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) {
+
+ update();
+ int x = p_event.mouse_button.x;
+ int total_w = get_size().width-get_size().height-3;
+ if (x>total_w+3) {
+
+ if (grabbed==-1)
+ return;
+ Size2 ms = Size2(350, picker->get_combined_minimum_size().height+10);
+ picker->set_color(points[grabbed].color);
+ popup->set_pos(get_global_pos()-Size2(0,ms.height));
+ popup->set_size(ms);
+ popup->popup();
+ return;
+ }
+ float ofs = CLAMP(x/float(total_w),0,1);
-void ShaderEditor::edit(Ref<Shader> p_shader) {
+ grabbed=-1;
+ grabbing=true;
+ int pos=-1;
+ for(int i=0;i<points.size();i++) {
+ if (ABS(x-points[i].offset*total_w)<4) {
+ grabbed=i;
+ }
+ if (points[i].offset<ofs)
+ pos=i;
+ }
- shader=p_shader;
+ grabbed_at=ofs;
+ //grab or select
+ if (grabbed!=-1) {
+ return;
+ }
+ //insert
- if (shader.is_null())
- hide();
- else {
- _read_shader_graph();
- }
-}
+ Point p;
+ p.offset=ofs;
-Size2 ShaderEditor::_get_maximum_size() {
+ Point prev;
+ Point next;
- Size2 max;
+ if (pos==-1) {
- for(List<int>::Element *E=order.front();E;E=E->next()) {
+ prev.color=Color(0,0,0);
+ prev.offset=0;
+ if (points.size()) {
+ next=points[0];
+ } else {
+ next.color=Color(1,1,1);
+ next.offset=1.0;
+ }
+ } else {
- Point2 pos = Point2( shader_graph.node_get_pos_x(E->get()), shader_graph.node_get_pos_y(E->get()) );
+ if (pos==points.size()-1) {
+ next.color=Color(1,1,1);
+ next.offset=1.0;
+ } else {
+ next=points[pos+1];
+ }
+ prev=points[pos];
- if (click_type==CLICK_NODE && click_node==E->get()) {
+ }
+
+ p.color=prev.color.linear_interpolate(next.color,(p.offset-prev.offset)/(next.offset-prev.offset));
- pos+=click_motion-click_pos;
+ points.push_back(p);
+ points.sort();
+ for(int i=0;i<points.size();i++) {
+ if (points[i].offset==ofs) {
+ grabbed=i;
+ break;
+ }
}
- pos+=get_node_size(E->get());
- if (pos.x>max.x)
- max.x=pos.x;
- if (pos.y>max.y)
- max.y=pos.y;
+
+ emit_signal("ramp_changed");
}
- return max;
-}
+ if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && !p_event.mouse_button.pressed) {
-Size2 ShaderEditor::get_node_size(int p_node) const {
+ if (grabbing) {
+ grabbing=false;
+ emit_signal("ramp_changed");
+ }
+ update();
+ }
- VisualServer::ShaderNodeType type=shader_graph.node_get_type(p_node);
- Ref<StyleBox> style = get_stylebox("panel","PopupMenu");
- Ref<Font> font = get_font("font","PopupMenu");
- Color font_color = get_color("font_color","PopupMenu");
+ if (p_event.type==InputEvent::MOUSE_MOTION && grabbing) {
- Size2 size = style->get_minimum_size();
+ int total_w = get_size().width-get_size().height-3;
- int count=1; // title
- count += VisualServer::shader_get_input_count( type) + VisualServer::shader_get_output_count( type);
+ int x = p_event.mouse_motion.x;
+ float newofs = CLAMP(x/float(total_w),0,1);
- float max_w=font->get_string_size( VisualServer::shader_node_get_type_info(type).name ).width;
+ bool valid=true;
+ for(int i=0;i<points.size();i++) {
- for(int i=0;i<VisualServer::shader_get_input_count(type);i++)
- max_w = MAX( max_w, font->get_string_size( VisualServer::shader_get_input_name(type,i) ).width );
+ if (points[i].offset==newofs && i!=grabbed) {
+ valid=false;
+ }
+ }
+ if (!valid)
+ return;
- for(int i=0;i<VisualServer::shader_get_output_count(type);i++)
- max_w = MAX( max_w, font->get_string_size( VisualServer::shader_get_output_name(type,i) ).width );
+ points[grabbed].offset=newofs;
+ points.sort();
+ for(int i=0;i<points.size();i++) {
+ if (points[i].offset==newofs) {
+ grabbed=i;
+ break;
+ }
+ }
+ emit_signal("ramp_changed");
+ update();
+ }
+}
- switch(type) {
+void GraphColorRampEdit::_notification(int p_what){
+
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+ picker->connect("color_changed",this,"_color_changed");
+ }
+ if (p_what==NOTIFICATION_DRAW) {
- case VS::NODE_IN:
- case VS::NODE_OUT:
- case VS::NODE_VEC_IN:
- case VS::NODE_VEC_OUT:
- case VS::NODE_PARAMETER:
- case VS::NODE_VEC_PARAMETER:
- case VS::NODE_COLOR_PARAMETER:
- case VS::NODE_TEXTURE_PARAMETER:
- case VS::NODE_TEXTURE_2D_PARAMETER:
- case VS::NODE_TEXTURE_CUBE_PARAMETER:
- case VS::NODE_TRANSFORM_PARAMETER:
- case VS::NODE_LABEL: {
- max_w=MAX( max_w, font->get_string_size( shader_graph.node_get_param(p_node) ).width );
- count++;
- } break;
- case VS::NODE_TIME:
- case VS::NODE_CONSTANT:
- case VS::NODE_VEC_CONSTANT:
- case VS::NODE_COLOR_CONSTANT:
- case VS::NODE_TRANSFORM_CONSTANT: {
- count++;
- } break;
- case VS::NODE_TEXTURE:
- case VS::NODE_VEC_TEXTURE_2D:
- case VS::NODE_VEC_TEXTURE_CUBE: {
+ Point prev;
+ prev.offset=0;
+ prev.color=Color(0,0,0);
+ int w = get_size().x;
+ int h = get_size().y;
- RefPtr res = shader_graph.node_get_param(p_node);
- Ref<Texture> texture = res;
- if (texture.is_null() || texture->get_width()==0) {
+ int total_w = get_size().width-get_size().height-3;
- size.y+=max_w;
+ for(int i=-1;i<points.size();i++) {
+
+ Point next;
+ if (i+1==points.size()) {
+ next.color=Color(1,1,1);
+ next.offset=1;
} else {
+ next=points[i+1];
+ }
- size.y+=max_w * texture->get_height() / texture->get_width();
+ if (prev.offset==next.offset) {
+ prev=next;
+ continue;
}
- } break;
- default: {}
+
+ Vector<Vector2> points;
+ Vector<Color> colors;
+ points.push_back(Vector2(prev.offset*total_w,h));
+ points.push_back(Vector2(prev.offset*total_w,0));
+ points.push_back(Vector2(next.offset*total_w,0));
+ points.push_back(Vector2(next.offset*total_w,h));
+ colors.push_back(prev.color);
+ colors.push_back(prev.color);
+ colors.push_back(next.color);
+ colors.push_back(next.color);
+ draw_primitive(points,colors,Vector<Point2>());
+ prev=next;
+ }
+
+ for(int i=0;i<points.size();i++) {
+
+ Color col=i==grabbed?Color(1,0.0,0.0,0.9):Color(1,1,1,0.8);
+
+ draw_line(Vector2(points[i].offset*total_w,0),Vector2(points[i].offset*total_w,h-1),Color(0,0,0,0.7));
+ draw_line(Vector2(points[i].offset*total_w-1,h/2),Vector2(points[i].offset*total_w-1,h-1),col);
+ draw_line(Vector2(points[i].offset*total_w+1,h/2),Vector2(points[i].offset*total_w+1,h-1),col);
+ draw_line(Vector2(points[i].offset*total_w-1,h/2),Vector2(points[i].offset*total_w+1,h/2),col);
+ draw_line(Vector2(points[i].offset*total_w-1,h-1),Vector2(points[i].offset*total_w+1,h-1),col);
+
+ }
+
+ if (grabbed!=-1) {
+
+ draw_rect(Rect2(total_w+3,0,h,h),points[grabbed].color);
+ }
+
+ if (has_focus()) {
+
+ draw_line(Vector2(-1,-1),Vector2(total_w+1,-1),Color(1,1,1,0.6));
+ draw_line(Vector2(total_w+1,-1),Vector2(total_w+1,h+1),Color(1,1,1,0.6));
+ draw_line(Vector2(total_w+1,h+1),Vector2(-1,h+1),Color(1,1,1,0.6));
+ draw_line(Vector2(-1,-1),Vector2(-1,h+1),Color(1,1,1,0.6));
+ }
}
+}
- size.x+=max_w;
- size.y+=count*(font->get_height()+get_constant("vseparation","PopupMenu"));
+Size2 GraphColorRampEdit::get_minimum_size() const {
- return size;
+ return Vector2(0,16);
}
-Error ShaderEditor::validate_graph() {
+void GraphColorRampEdit::_color_changed(const Color& p_color) {
+
+ if (grabbed==-1)
+ return;
+ points[grabbed].color=p_color;
+ update();
+ emit_signal("ramp_changed");
- _ShaderTester st;
- active_nodes.clear();
- st._set=&active_nodes;
- return shader_graph.generate(&st);
}
-void ShaderEditor::_draw_node(int p_node) {
+void GraphColorRampEdit::set_ramp(const Vector<float>& p_offsets,const Vector<Color>& p_colors) {
+
+ ERR_FAIL_COND(p_offsets.size()!=p_colors.size());
+ points.clear();
+ for(int i=0;i<p_offsets.size();i++) {
+ Point p;
+ p.offset=p_offsets[i];
+ p.color=p_colors[i];
+ points.push_back(p);
+ }
- VisualServer::ShaderNodeType type=shader_graph.node_get_type(p_node);
- Ref<StyleBox> style = active_nodes.has(p_node)?get_stylebox("panel","PopupMenu"):get_stylebox("panel_disabled","PopupMenu");
- Ref<Font> font = get_font("font","PopupMenu");
- Color font_color = get_color("font_color","PopupMenu");
- Color font_color_title = get_color("font_color_hover","PopupMenu");
- Size2 size=get_node_size(p_node);
- Point2 pos = Point2( shader_graph.node_get_pos_x(p_node), shader_graph.node_get_pos_y(p_node) )-offset;
+ points.sort();
+ update();
+}
- if (click_type==CLICK_NODE && click_node==p_node) {
+Vector<float> GraphColorRampEdit::get_offsets() const{
+ Vector<float> ret;
+ for(int i=0;i<points.size();i++)
+ ret.push_back(points[i].offset);
+ return ret;
+}
+Vector<Color> GraphColorRampEdit::get_colors() const{
- pos+=click_motion-click_pos;
+ Vector<Color> ret;
+ for(int i=0;i<points.size();i++)
+ ret.push_back(points[i].color);
+ return ret;
+}
+
+
+void GraphColorRampEdit::_bind_methods(){
+
+ ObjectTypeDB::bind_method(_MD("_input_event"),&GraphColorRampEdit::_input_event);
+ ObjectTypeDB::bind_method(_MD("_color_changed"),&GraphColorRampEdit::_color_changed);
+ ADD_SIGNAL(MethodInfo("ramp_changed"));
+}
+
+GraphColorRampEdit::GraphColorRampEdit(){
+
+ grabbed=-1;
+ grabbing=false;
+ set_focus_mode(FOCUS_ALL);
+
+ popup = memnew( PopupPanel );
+ picker = memnew( ColorPicker );
+ popup->add_child(picker);
+ popup->set_child_rect(picker);
+ add_child(popup);
+
+}
+////////////
+
+void GraphCurveMapEdit::_input_event(const InputEvent& p_event) {
+
+ if (p_event.type==InputEvent::KEY && p_event.key.pressed && p_event.key.scancode==KEY_DELETE && grabbed!=-1) {
+
+ points.remove(grabbed);
+ grabbed=-1;
+ update();
+ emit_signal("curve_changed");
+ accept_event();
}
- RID ci = get_canvas_item();
- style->draw(ci,Rect2(pos,size));
+ if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) {
+
+ update();
+ Point2 p = Vector2(p_event.mouse_button.x,p_event.mouse_button.y)/get_size();
+ p.y=1.0-p.y;
+ grabbed=-1;
+ grabbing=true;
- Point2 ofs=style->get_offset()+pos;
- Point2 ascent=Point2(0,font->get_ascent());
- float w = size.width-style->get_minimum_size().width;
- float h = font->get_height()+get_constant("vseparation","PopupMenu");
+ for(int i=0;i<points.size();i++) {
- font->draw_halign( ci, ofs+ascent, HALIGN_CENTER,w, VisualServer::shader_node_get_type_info(type).name,font_color_title);
- ofs.y+=h;
+ Vector2 ps = p*get_size();
+ Vector2 pt = Vector2(points[i].offset,points[i].height)*get_size();
+ if (ps.distance_to(pt)<4) {
+ grabbed=i;
+ }
- Ref<Texture> vec_icon = get_icon("NodeVecSlot","EditorIcons");
- Ref<Texture> real_icon = get_icon("NodeRealSlot","EditorIcons");
- float icon_h_ofs = Math::floor(( font->get_height()-vec_icon->get_height())/2.0 )+1;
+ }
+
+
+ //grab or select
+ if (grabbed!=-1) {
+ return;
+ }
+ //insert
- for(int i=0;i<VisualServer::shader_get_input_count(type);i++) {
+ Point np;
+ np.offset=p.x;
+ np.height=p.y;
+
+ points.push_back(np);
+ points.sort();
+ for(int i=0;i<points.size();i++) {
+ if (points[i].offset==p.x && points[i].height==p.y) {
+ grabbed=i;
+ break;
+ }
+ }
+
+ emit_signal("curve_changed");
- String name = VisualServer::shader_get_input_name(type,i);
- font->draw_halign( ci, ofs+ascent, HALIGN_LEFT,w, name,font_color);
- Ref<Texture> icon = VisualServer::shader_is_input_vector(type,i)?vec_icon:real_icon;
- icon->draw(ci,ofs+Point2(-real_icon->get_width(),icon_h_ofs));
- ofs.y+=h;
}
- for(int i=0;i<VisualServer::shader_get_output_count(type);i++) {
+ if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && !p_event.mouse_button.pressed) {
- String name = VisualServer::shader_get_output_name(type,i);
- font->draw_halign( ci, ofs+ascent, HALIGN_RIGHT,w, name,font_color);
- Ref<Texture> icon = VisualServer::shader_is_output_vector(type,i)?vec_icon:real_icon;
- icon->draw(ci,ofs+Point2(w,icon_h_ofs));
- ofs.y+=h;
+ if (grabbing) {
+ grabbing=false;
+ emit_signal("curve_changed");
+ }
+ update();
}
- switch(type) {
-
- case VS::NODE_IN:
- case VS::NODE_OUT:
- case VS::NODE_PARAMETER:
- case VS::NODE_VEC_IN:
- case VS::NODE_COLOR_PARAMETER:
- case VS::NODE_VEC_OUT:
- case VS::NODE_TEXTURE_PARAMETER:
- case VS::NODE_TEXTURE_2D_PARAMETER:
- case VS::NODE_TEXTURE_CUBE_PARAMETER:
- case VS::NODE_TRANSFORM_CONSTANT:
- case VS::NODE_TRANSFORM_PARAMETER:
- case VS::NODE_VEC_PARAMETER:
- case VS::NODE_LABEL: {
- String text = shader_graph.node_get_param(p_node);
- font->draw_halign( ci, ofs+ascent, HALIGN_CENTER,w, text,font_color);
- } break;
- case VS::NODE_TIME:
- case VS::NODE_CONSTANT: {
- String text = rtos(shader_graph.node_get_param(p_node));
- font->draw_halign( ci, ofs+ascent, HALIGN_CENTER,w, text,font_color);
-
- } break;
- case VS::NODE_VEC_CONSTANT: {
- String text = Vector3(shader_graph.node_get_param(p_node));
- font->draw_halign( ci, ofs+ascent, HALIGN_CENTER,w, text,font_color);
- } break;
- case VS::NODE_COLOR_CONSTANT: {
-
- Color color = shader_graph.node_get_param(p_node);
- Rect2 r(ofs,Size2(w,h));
- VisualServer::get_singleton()->canvas_item_add_rect(ci,r,color);
- } break;
- case VS::NODE_TEXTURE:
- case VS::NODE_VEC_TEXTURE_2D:
- case VS::NODE_VEC_TEXTURE_CUBE: {
-
- Rect2 r(ofs,Size2(w,(pos.y+size.y-style->get_margin(MARGIN_BOTTOM))-ofs.y));
- Vector<Point2> points;
- Vector<Point2> uvs;
- points.resize(4);
- uvs.resize(4);
- points[0]=r.pos;
- points[1]=r.pos+Point2(r.size.x,0);
- points[2]=r.pos+r.size;
- points[3]=r.pos+Point2(0,r.size.y);
- uvs[0]=Point2(0,0);
- uvs[1]=Point2(1,0);
- uvs[2]=Point2(1,1);
- uvs[3]=Point2(0,1);
-
- Ref<Texture> texture = shader_graph.node_get_param(p_node).operator RefPtr();
- if (texture.is_null() || texture->get_width()==0) {
- texture=get_icon("Click2Edit","EditorIcons");
+ if (p_event.type==InputEvent::MOUSE_MOTION && grabbing && grabbed != -1) {
+
+ Point2 p = Vector2(p_event.mouse_button.x,p_event.mouse_button.y)/get_size();
+ p.y=1.0-p.y;
+
+ p.x = CLAMP(p.x,0.0,1.0);
+ p.y = CLAMP(p.y,0.0,1.0);
+
+ bool valid=true;
+
+ for(int i=0;i<points.size();i++) {
+
+ if (points[i].offset==p.x && points[i].height==p.y && i!=grabbed) {
+ valid=false;
}
+ }
+
+ if (!valid)
+ return;
+
+ points[grabbed].offset=p.x;
+ points[grabbed].height=p.y;
- VisualServer::get_singleton()->canvas_item_add_primitive(ci,points,Vector<Color>(),uvs,texture->get_rid());
- } break;
- default: {}
+ points.sort();
+ for(int i=0;i<points.size();i++) {
+ if (points[i].offset==p.x && points[i].height==p.y) {
+ grabbed=i;
+ break;
+ }
+ }
+
+ emit_signal("curve_changed");
+
+ update();
}
}
-void ShaderEditor::_node_param_changed() {
+void GraphCurveMapEdit::_plot_curve(const Vector2& p_a,const Vector2& p_b,const Vector2& p_c,const Vector2& p_d) {
+
+ float geometry[4][4];
+ float tmp1[4][4];
+ float tmp2[4][4];
+ float deltas[4][4];
+ double x, dx, dx2, dx3;
+ double y, dy, dy2, dy3;
+ double d, d2, d3;
+ int lastx, lasty;
+ int newx, newy;
+ int ntimes;
+ int i,j;
+
+ int xmax=get_size().x;
+ int ymax=get_size().y;
+
+ /* construct the geometry matrix from the segment */
+ for (i = 0; i < 4; i++) {
+ geometry[i][2] = 0;
+ geometry[i][3] = 0;
+ }
- shader_graph.node_set_param( click_node,property_editor->get_variant() );
- update();
- _write_shader_graph();
-}
+ geometry[0][0] = (p_a[0] * xmax);
+ geometry[1][0] = (p_b[0] * xmax);
+ geometry[2][0] = (p_c[0] * xmax);
+ geometry[3][0] = (p_d[0] * xmax);
+
+ geometry[0][1] = (p_a[1] * ymax);
+ geometry[1][1] = (p_b[1] * ymax);
+ geometry[2][1] = (p_c[1] * ymax);
+ geometry[3][1] = (p_d[1] * ymax);
+
+ /* subdivide the curve ntimes (1000) times */
+ ntimes = 4 * xmax;
+ /* ntimes can be adjusted to give a finer or coarser curve */
+ d = 1.0 / ntimes;
+ d2 = d * d;
+ d3 = d * d * d;
+
+ /* construct a temporary matrix for determining the forward differencing deltas */
+ tmp2[0][0] = 0; tmp2[0][1] = 0; tmp2[0][2] = 0; tmp2[0][3] = 1;
+ tmp2[1][0] = d3; tmp2[1][1] = d2; tmp2[1][2] = d; tmp2[1][3] = 0;
+ tmp2[2][0] = 6*d3; tmp2[2][1] = 2*d2; tmp2[2][2] = 0; tmp2[2][3] = 0;
+ tmp2[3][0] = 6*d3; tmp2[3][1] = 0; tmp2[3][2] = 0; tmp2[3][3] = 0;
+
+ /* compose the basis and geometry matrices */
+
+ static const float CR_basis[4][4] =
+ {
+ { -0.5, 1.5, -1.5, 0.5 },
+ { 1.0, -2.5, 2.0, -0.5 },
+ { -0.5, 0.0, 0.5, 0.0 },
+ { 0.0, 1.0, 0.0, 0.0 },
+ };
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ tmp1[i][j] = (CR_basis[i][0] * geometry[0][j] +
+ CR_basis[i][1] * geometry[1][j] +
+ CR_basis[i][2] * geometry[2][j] +
+ CR_basis[i][3] * geometry[3][j]);
+ }
+ }
+ /* compose the above results to get the deltas matrix */
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ deltas[i][j] = (tmp2[i][0] * tmp1[0][j] +
+ tmp2[i][1] * tmp1[1][j] +
+ tmp2[i][2] * tmp1[2][j] +
+ tmp2[i][3] * tmp1[3][j]);
+ }
+ }
-ShaderEditor::ClickType ShaderEditor::_locate_click(const Point2& p_click,int *p_node_id,int *p_slot_index) const {
- Ref<StyleBox> style = get_stylebox("panel","PopupMenu");
- Ref<Texture> real_icon = get_icon("NodeRealSlot","EditorIcons");
- Ref<Font> font = get_font("font","PopupMenu");
- float h = font->get_height()+get_constant("vseparation","PopupMenu");
- float extra_left=MAX( real_icon->get_width()-style->get_margin(MARGIN_LEFT), 0 );
- float extra_right=MAX( real_icon->get_width()-style->get_margin(MARGIN_RIGHT), 0 );
+ /* extract the x deltas */
+ x = deltas[0][0];
+ dx = deltas[1][0];
+ dx2 = deltas[2][0];
+ dx3 = deltas[3][0];
+ /* extract the y deltas */
+ y = deltas[0][1];
+ dy = deltas[1][1];
+ dy2 = deltas[2][1];
+ dy3 = deltas[3][1];
- for(const List<int>::Element *E=order.back();E;E=E->prev()) {
- Size2 size=get_node_size(E->get());
- size.width+=extra_left+extra_right;
- Point2 pos = Point2( shader_graph.node_get_pos_x(E->get()), shader_graph.node_get_pos_y(E->get()) )-offset;
- pos.x-=extra_left;
+ lastx = CLAMP (x, 0, xmax);
+ lasty = CLAMP (y, 0, ymax);
- Rect2 rect( pos, size );
- if (!rect.has_point(p_click))
- continue;
- VisualServer::ShaderNodeType type=shader_graph.node_get_type(E->get());
- if (p_node_id)
- *p_node_id=E->get();
- float y=p_click.y-(pos.y+style->get_margin(MARGIN_TOP));
- if (y<h)
- return CLICK_NODE;
- y-=h;
-
- for(int i=0;i<VisualServer::shader_get_input_count(type);i++) {
-
- if (y<h) {
- if (p_slot_index)
- *p_slot_index=i;
- return CLICK_INPUT_SLOT;
+ /* if (fix255)
+ {
+ cd->curve[cd->outline][lastx] = lasty;
+ }
+ else
+ {
+ cd->curve_ptr[cd->outline][lastx] = lasty;
+ if(gb_debug) printf("bender_plot_curve xmax:%d ymax:%d\n", (int)xmax, (int)ymax);
+ }
+*/
+ /* loop over the curve */
+ for (i = 0; i < ntimes; i++)
+ {
+ /* increment the x values */
+ x += dx;
+ dx += dx2;
+ dx2 += dx3;
+
+ /* increment the y values */
+ y += dy;
+ dy += dy2;
+ dy2 += dy3;
+
+ newx = CLAMP ((Math::round (x)), 0, xmax);
+ newy = CLAMP ((Math::round (y)), 0, ymax);
+
+ /* if this point is different than the last one...then draw it */
+ if ((lastx != newx) || (lasty != newy))
+ {
+#if 0
+ if(fix255)
+ {
+ /* use fixed array size (for the curve graph) */
+ cd->curve[cd->outline][newx] = newy;
}
- y-=h;
+ else
+ {
+ /* use dynamic allocated curve_ptr (for the real curve) */
+ cd->curve_ptr[cd->outline][newx] = newy;
+
+ if(gb_debug) printf("outline: %d cX: %d cY: %d\n", (int)cd->outline, (int)newx, (int)newy);
+ }
+#endif
+ draw_line(Vector2(lastx,ymax-lasty),Vector2(newx,ymax-newy),Color(0.8,0.8,0.8,0.8),2.0);
}
- for(int i=0;i<VisualServer::shader_get_output_count(type);i++) {
+ lastx = newx;
+ lasty = newy;
+ }
+}
+
+
+void GraphCurveMapEdit::_notification(int p_what){
+
+ if (p_what==NOTIFICATION_DRAW) {
+
+ draw_style_box(get_stylebox("bg","Tree"),Rect2(Point2(),get_size()));
+
+ int w = get_size().x;
+ int h = get_size().y;
- if (y<h) {
- if (p_slot_index)
- *p_slot_index=i;
- return CLICK_OUTPUT_SLOT;
+ Vector2 prev=Vector2(0,0);
+ Vector2 prev2=Vector2(0,0);
+
+ for(int i=-1;i<points.size();i++) {
+
+ Vector2 next;
+ Vector2 next2;
+ if (i+1>=points.size()) {
+ next=Vector2(1,1);
+ } else {
+ next=Vector2(points[i+1].offset,points[i+1].height);
}
- y-=h;
+
+ if (i+2>=points.size()) {
+ next2=Vector2(1,1);
+ } else {
+ next2=Vector2(points[i+2].offset,points[i+2].height);
+ }
+
+ /*if (i==-1 && prev.offset==next.offset) {
+ prev=next;
+ continue;
+ }*/
+
+ _plot_curve(prev2,prev,next,next2);
+
+ prev2=prev;
+ prev=next;
}
- if (p_click.y<(rect.pos.y+rect.size.height-style->get_margin(MARGIN_BOTTOM)))
- return CLICK_PARAMETER;
- else
- return CLICK_NODE;
+ for(int i=0;i<points.size();i++) {
+
+ Color col=i==grabbed?Color(1,0.0,0.0,0.9):Color(1,1,1,0.8);
+
+
+ draw_rect(Rect2( Vector2(points[i].offset,1.0-points[i].height)*get_size()-Vector2(2,2),Vector2(5,5)),col);
+ }
+
+ /* if (grabbed!=-1) {
+
+ draw_rect(Rect2(total_w+3,0,h,h),points[grabbed].color);
+ }
+*/
+ if (has_focus()) {
+
+ draw_line(Vector2(-1,-1),Vector2(w+1,-1),Color(1,1,1,0.6));
+ draw_line(Vector2(w+1,-1),Vector2(w+1,h+1),Color(1,1,1,0.6));
+ draw_line(Vector2(w+1,h+1),Vector2(-1,h+1),Color(1,1,1,0.6));
+ draw_line(Vector2(-1,-1),Vector2(-1,h+1),Color(1,1,1,0.6));
+ }
}
+}
- return CLICK_NONE;
+Size2 GraphCurveMapEdit::get_minimum_size() const {
+ return Vector2(64,64);
}
-Point2 ShaderEditor::_get_slot_pos(int p_node_id,bool p_input,int p_slot) {
- Ref<StyleBox> style = get_stylebox("panel","PopupMenu");
- float w = get_node_size(p_node_id).width;
- Ref<Font> font = get_font("font","PopupMenu");
- float h = font->get_height()+get_constant("vseparation","PopupMenu");
- Ref<Texture> vec_icon = get_icon("NodeVecSlot","EditorIcons");
- Point2 pos = Point2( shader_graph.node_get_pos_x(p_node_id), shader_graph.node_get_pos_y(p_node_id) )-offset;
- pos+=style->get_offset();
- pos.y+=h;
- if(p_input) {
+void GraphCurveMapEdit::set_points(const Vector<Vector2>& p_points) {
- pos.y+=p_slot*h;
- pos+=Point2( -vec_icon->get_width()/2.0, h/2.0).floor();
- return pos;
- } else {
- pos.y+=VisualServer::shader_get_input_count( shader_graph.node_get_type(p_node_id ) )*h;
+ points.clear();
+ for(int i=0;i<p_points.size();i++) {
+ Point p;
+ p.offset=p_points[i].x;
+ p.height=p_points[i].y;
+ points.push_back(p);
}
- pos.y+=p_slot*h;
- pos+=Point2( w-style->get_minimum_size().width+vec_icon->get_width()/2.0, h/2.0).floor();
-
- return pos;
+ points.sort();
+ update();
+}
+Vector<Vector2> GraphCurveMapEdit::get_points() const {
+ Vector<Vector2> ret;
+ for(int i=0;i<points.size();i++)
+ ret.push_back(Vector2(points[i].offset,points[i].height));
+ return ret;
}
-void ShaderEditor::_node_edit_property(int p_node) {
+void GraphCurveMapEdit::_bind_methods(){
- Ref<StyleBox> style = get_stylebox("panel","PopupMenu");
- Size2 size = get_node_size(p_node);
- Point2 pos = Point2( shader_graph.node_get_pos_x(p_node), shader_graph.node_get_pos_y(p_node) )-offset;
+ ObjectTypeDB::bind_method(_MD("_input_event"),&GraphCurveMapEdit::_input_event);
+ ADD_SIGNAL(MethodInfo("curve_changed"));
+}
- VisualServer::ShaderNodeType type=shader_graph.node_get_type(p_node);
+GraphCurveMapEdit::GraphCurveMapEdit(){
- PropertyInfo ph = VisualServer::get_singleton()->shader_node_get_type_info(type);
- if (ph.type==Variant::NIL)
- return;
- if (ph.type==Variant::_RID)
- ph.type=Variant::OBJECT;
+ grabbed=-1;
+ grabbing=false;
+ set_focus_mode(FOCUS_ALL);
- property_editor->edit(NULL,ph.name,ph.type,shader_graph.node_get_param(p_node),ph.hint,ph.hint_string);
+}
- Point2 popup_pos=Point2( pos.x+(size.width-property_editor->get_size().width)/2.0,pos.y+(size.y-style->get_margin(MARGIN_BOTTOM))).floor();
- popup_pos+=get_global_pos();
- property_editor->set_pos(popup_pos);
- property_editor->popup();
+////cbacks
+///
+void ShaderGraphView::_scalar_const_changed(double p_value,int p_id) {
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change Scalar Constant",true);
+ ur->add_do_method(graph.ptr(),"scalar_const_node_set_value",type,p_id,p_value);
+ ur->add_undo_method(graph.ptr(),"scalar_const_node_set_value",type,p_id,graph->scalar_const_node_get_value(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
}
-bool ShaderEditor::has_point(const Point2& p_point) const {
+void ShaderGraphView::_vec_const_changed(double p_value, int p_id,Array p_arr){
+
+ Vector3 val;
+ for(int i=0;i<p_arr.size();i++) {
+ val[i]=p_arr[i].call("get_val");
+ }
- int n,si;
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change Vec Constant",true);
+ ur->add_do_method(graph.ptr(),"vec_const_node_set_value",type,p_id,val);
+ ur->add_undo_method(graph.ptr(),"vec_const_node_set_value",type,p_id,graph->vec_const_node_get_value(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
- return _locate_click(p_point,&n,&si)!=CLICK_NONE;
}
+void ShaderGraphView::_rgb_const_changed(const Color& p_color, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change RGB Constant",true);
+ ur->add_do_method(graph.ptr(),"rgb_const_node_set_value",type,p_id,p_color);
+ ur->add_undo_method(graph.ptr(),"rgb_const_node_set_value",type,p_id,graph->rgb_const_node_get_value(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
-void ShaderEditor::_input_event(InputEvent p_event) {
+}
+void ShaderGraphView::_scalar_op_changed(int p_op, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change Scalar Operator");
+ ur->add_do_method(graph.ptr(),"scalar_op_node_set_op",type,p_id,p_op);
+ ur->add_undo_method(graph.ptr(),"scalar_op_node_set_op",type,p_id,graph->scalar_op_node_get_op(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
- switch(p_event.type) {
+}
+void ShaderGraphView::_vec_op_changed(int p_op, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change Vec Operator");
+ ur->add_do_method(graph.ptr(),"vec_op_node_set_op",type,p_id,p_op);
+ ur->add_undo_method(graph.ptr(),"vec_op_node_set_op",type,p_id,graph->vec_op_node_get_op(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+void ShaderGraphView::_vec_scalar_op_changed(int p_op, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change VecxScalar Operator");
+ ur->add_do_method(graph.ptr(),"vec_scalar_op_node_set_op",type,p_id,p_op);
+ ur->add_undo_method(graph.ptr(),"vec_scalar_op_node_set_op",type,p_id,graph->vec_scalar_op_node_get_op(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
- case InputEvent::MOUSE_BUTTON: {
+}
+void ShaderGraphView::_rgb_op_changed(int p_op, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change RGB Operator");
+ ur->add_do_method(graph.ptr(),"rgb_op_node_set_op",type,p_id,p_op);
+ ur->add_undo_method(graph.ptr(),"rgb_op_node_set_op",type,p_id,graph->rgb_op_node_get_op(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+void ShaderGraphView::_xform_inv_rev_changed(bool p_enabled, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Toggle Rot Only");
+ ur->add_do_method(graph.ptr(),"xform_vec_mult_node_set_no_translation",type,p_id,p_enabled);
+ ur->add_undo_method(graph.ptr(),"xform_vec_mult_node_set_no_translation",type,p_id,graph->xform_vec_mult_node_get_no_translation(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+void ShaderGraphView::_scalar_func_changed(int p_func, int p_id){
+
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change Scalar Function");
+ ur->add_do_method(graph.ptr(),"scalar_func_node_set_function",type,p_id,p_func);
+ ur->add_undo_method(graph.ptr(),"scalar_func_node_set_function",type,p_id,graph->scalar_func_node_get_function(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+void ShaderGraphView::_vec_func_changed(int p_func, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change Vec Function");
+ ur->add_do_method(graph.ptr(),"vec_func_node_set_function",type,p_id,p_func);
+ ur->add_undo_method(graph.ptr(),"vec_func_node_set_function",type,p_id,graph->vec_func_node_get_function(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
- if (p_event.mouse_button.pressed) {
+}
+void ShaderGraphView::_scalar_input_changed(double p_value,int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change Scalar Uniform",true);
+ ur->add_do_method(graph.ptr(),"scalar_input_node_set_value",type,p_id,p_value);
+ ur->add_undo_method(graph.ptr(),"scalar_input_node_set_value",type,p_id,graph->scalar_input_node_get_value(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+void ShaderGraphView::_vec_input_changed(double p_value, int p_id,Array p_arr){
- if (p_event.mouse_button.button_index==1) {
- click_pos=Point2(p_event.mouse_button.x,p_event.mouse_button.y);
- click_motion=click_pos;
- click_type = _locate_click(click_pos,&click_node,&click_slot);
- if( click_type!=CLICK_NONE) {
+ Vector3 val;
+ for(int i=0;i<p_arr.size();i++) {
+ val[i]=p_arr[i].call("get_val");
+ }
- order.erase(click_node);
- order.push_back(click_node);
- update();
- }
- switch(click_type) {
- case CLICK_INPUT_SLOT: {
- click_pos=_get_slot_pos(click_node,true,click_slot);
- } break;
- case CLICK_OUTPUT_SLOT: {
- click_pos=_get_slot_pos(click_node,false,click_slot);
- } break;
- case CLICK_PARAMETER: {
- //open editor
- _node_edit_property(click_node);
- } break;
- }
- }
- if (p_event.mouse_button.button_index==2) {
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change Vec Uniform",true);
+ ur->add_do_method(graph.ptr(),"vec_input_node_set_value",type,p_id,val);
+ ur->add_undo_method(graph.ptr(),"vec_input_node_set_value",type,p_id,graph->vec_input_node_get_value(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
- if (click_type!=CLICK_NONE) {
- click_type=CLICK_NONE;
- update();
- } else {
- // try to disconnect/remove
+}
+void ShaderGraphView::_xform_input_changed(int p_id, Node *p_button){
- Point2 rclick_pos=Point2(p_event.mouse_button.x,p_event.mouse_button.y);
- rclick_type = _locate_click(rclick_pos,&rclick_node,&rclick_slot);
- if (rclick_type==CLICK_INPUT_SLOT || rclick_type==CLICK_OUTPUT_SLOT) {
- node_popup->clear();
- node_popup->add_item("Disconnect",NODE_DISCONNECT);
- node_popup->set_pos(rclick_pos);
- node_popup->popup();
+ ToolButton *tb = p_button->cast_to<ToolButton>();
+ ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height));
+ ped_popup->set_size(tb->get_size());
+ edited_id=p_id;
+ edited_def=-1;
+ ped_popup->edit(NULL,"",Variant::TRANSFORM,graph->xform_input_node_get_value(type,p_id),PROPERTY_HINT_NONE,"");
+ ped_popup->popup();
- }
+}
+void ShaderGraphView::_xform_const_changed(int p_id, Node *p_button){
- if (rclick_type==CLICK_NODE) {
- node_popup->clear();
- node_popup->add_item("Remove",NODE_ERASE);
- node_popup->set_pos(rclick_pos);
- node_popup->popup();
- }
+ ToolButton *tb = p_button->cast_to<ToolButton>();
+ ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height));
+ ped_popup->set_size(tb->get_size());
+ edited_id=p_id;
+ edited_def=-1;
+ ped_popup->edit(NULL,"",Variant::TRANSFORM,graph->xform_const_node_get_value(type,p_id),PROPERTY_HINT_NONE,"");
+ ped_popup->popup();
+}
- }
- }
- } else {
+void ShaderGraphView::_rgb_input_changed(const Color& p_color, int p_id){
- if (p_event.mouse_button.button_index==1 && click_type!=CLICK_NONE) {
- switch(click_type) {
- case CLICK_INPUT_SLOT:
- case CLICK_OUTPUT_SLOT: {
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change RGB Uniform",true);
+ ur->add_do_method(graph.ptr(),"rgb_input_node_set_value",type,p_id,p_color);
+ ur->add_undo_method(graph.ptr(),"rgb_input_node_set_value",type,p_id,graph->rgb_input_node_get_value(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+void ShaderGraphView::_tex_input_change(int p_id, Node *p_button){
- Point2 dst_click_pos=Point2(p_event.mouse_button.x,p_event.mouse_button.y);
- int id;
- int slot;
- ClickType dst_click_type = _locate_click(dst_click_pos,&id,&slot);
- if (dst_click_type==CLICK_INPUT_SLOT && click_type==CLICK_OUTPUT_SLOT) {
- shader_graph.connect(click_node,click_slot,id,slot);
+}
+void ShaderGraphView::_cube_input_change(int p_id){
- Error err = validate_graph();
- if (err==ERR_CYCLIC_LINK)
- shader_graph.disconnect(click_node,click_slot,id,slot);
- _write_shader_graph();
- }
- if (click_type==CLICK_INPUT_SLOT && dst_click_type==CLICK_OUTPUT_SLOT) {
+}
- shader_graph.connect(id,slot,click_node,click_slot);
+void ShaderGraphView::_variant_edited() {
+
+ if (edited_def != -1) {
+
+ Variant v = ped_popup->get_variant();
+ Variant v2 = graph->default_get_value(type,edited_id,edited_def);
+ if (v2.get_type() == Variant::NIL)
+ switch (v.get_type()) {
+ case Variant::VECTOR3:
+ v2=Vector3();
+ break;
+ case Variant::REAL:
+ v2=0.0;
+ break;
+ case Variant::TRANSFORM:
+ v2=Transform();
+ break;
+ case Variant::COLOR:
+ v2=Color();
+ break;
+ }
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change default value");
+ ur->add_do_method(graph.ptr(),"default_set_value",type,edited_id,edited_def, v);
+ ur->add_undo_method(graph.ptr(),"default_set_value",type,edited_id,edited_def, v2);
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+ return;
+ }
- Error err = validate_graph();
- if (err==ERR_CYCLIC_LINK)
- shader_graph.disconnect(id,slot,click_node,click_slot);
- _write_shader_graph();
- }
+ if (graph->node_get_type(type,edited_id)==ShaderGraph::NODE_XFORM_CONST) {
- } break;
- case CLICK_NODE: {
- int new_x=shader_graph.node_get_pos_x(click_node)+(click_motion.x-click_pos.x);
- int new_y=shader_graph.node_get_pos_y(click_node)+(click_motion.y-click_pos.y);
- shader_graph.node_set_pos(click_node,new_x,new_y);
- _write_shader_graph();
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change XForm Uniform");
+ ur->add_do_method(graph.ptr(),"xform_const_node_set_value",type,edited_id,ped_popup->get_variant());
+ ur->add_undo_method(graph.ptr(),"xform_const_node_set_value",type,edited_id,graph->xform_const_node_get_value(type,edited_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+ }
- } break;
- }
- click_type=CLICK_NONE;
- update();
- }
- }
+ if (graph->node_get_type(type,edited_id)==ShaderGraph::NODE_XFORM_INPUT) {
- }
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change XForm Uniform");
+ ur->add_do_method(graph.ptr(),"xform_input_node_set_value",type,edited_id,ped_popup->get_variant());
+ ur->add_undo_method(graph.ptr(),"xform_input_node_set_value",type,edited_id,graph->xform_input_node_get_value(type,edited_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+ }
- case InputEvent::MOUSE_MOTION: {
+ if (graph->node_get_type(type,edited_id)==ShaderGraph::NODE_TEXTURE_INPUT) {
- if (p_event.mouse_motion.button_mask&1 && click_type!=CLICK_NONE) {
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change Texture Uniform");
+ ur->add_do_method(graph.ptr(),"texture_input_node_set_value",type,edited_id,ped_popup->get_variant());
+ ur->add_undo_method(graph.ptr(),"texture_input_node_set_value",type,edited_id,graph->texture_input_node_get_value(type,edited_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+ }
- click_motion=Point2(p_event.mouse_button.x,p_event.mouse_button.y);
- update();
- }
+ if (graph->node_get_type(type,edited_id)==ShaderGraph::NODE_CUBEMAP_INPUT) {
- } break;
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change Cubemap Uniform");
+ ur->add_do_method(graph.ptr(),"cubemap_input_node_set_value",type,edited_id,ped_popup->get_variant());
+ ur->add_undo_method(graph.ptr(),"cubemap_input_node_set_value",type,edited_id,graph->cubemap_input_node_get_value(type,edited_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
}
+
}
-void ShaderEditor::_notification(int p_what) {
+void ShaderGraphView::_comment_edited(int p_id,Node* p_button) {
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ TextEdit *te=p_button->cast_to<TextEdit>();
+ ur->create_action("Change Comment",true);
+ ur->add_do_method(graph.ptr(),"comment_node_set_text",type,p_id,te->get_text());
+ ur->add_undo_method(graph.ptr(),"comment_node_set_text",type,p_id,graph->comment_node_get_text(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
- switch(p_what) {
+}
- case NOTIFICATION_DRAW: {
+void ShaderGraphView::_color_ramp_changed(int p_id,Node* p_ramp) {
- _update_scrollbars();
- //VisualServer::get_singleton()->canvas_item_add_rect(get_canvas_item(),Rect2(Point2(),get_size()),Color(0,0,0,1));
+ GraphColorRampEdit *cr=p_ramp->cast_to<GraphColorRampEdit>();
- for(List<int>::Element *E=order.front();E;E=E->next()) {
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
- _draw_node(E->get());
- }
- if (click_type==CLICK_INPUT_SLOT || click_type==CLICK_OUTPUT_SLOT) {
+ Vector<float> offsets=cr->get_offsets();
+ Vector<Color> colors=cr->get_colors();
- VisualServer::get_singleton()->canvas_item_add_line(get_canvas_item(),click_pos,click_motion,Color(0.5,1,0.5,0.8),2);
- }
+ DVector<float> new_offsets;
+ DVector<Color> new_colors;
+ {
+ new_offsets.resize(offsets.size());
+ new_colors.resize(colors.size());
+ DVector<float>::Write ow=new_offsets.write();
+ DVector<Color>::Write cw=new_colors.write();
+ for(int i=0;i<new_offsets.size();i++) {
+ ow[i]=offsets[i];
+ cw[i]=colors[i];
+ }
- List<ShaderGraph::Connection> connections = shader_graph.get_connection_list();
- for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) {
+ }
- const ShaderGraph::Connection &c=E->get();
- Point2 source = _get_slot_pos(c.src_id,false,c.src_slot);
- Point2 dest = _get_slot_pos(c.dst_id,true,c.dst_slot);
- bool vec = VisualServer::shader_is_input_vector( shader_graph.node_get_type(c.dst_id), c.dst_slot );
- Color col = vec?Color(1,0.5,0.5,0.8):Color(1,1,0.5,0.8);
- if (click_type==CLICK_NODE && click_node==c.src_id) {
+ DVector<float> old_offsets=graph->color_ramp_node_get_offsets(type,p_id);
+ DVector<Color> old_colors=graph->color_ramp_node_get_colors(type,p_id);
- source+=click_motion-click_pos;
- }
+ if (old_offsets.size()!=new_offsets.size())
+ ur->create_action("Add/Remove to Color Ramp");
+ else
+ ur->create_action("Modify Color Ramp",true);
- if (click_type==CLICK_NODE && click_node==c.dst_id) {
+ ur->add_do_method(graph.ptr(),"color_ramp_node_set_ramp",type,p_id,new_colors,new_offsets);
+ ur->add_undo_method(graph.ptr(),"color_ramp_node_set_ramp",type,p_id,old_colors,old_offsets);
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
- dest+=click_motion-click_pos;
- }
+void ShaderGraphView::_curve_changed(int p_id,Node* p_curve) {
- VisualServer::get_singleton()->canvas_item_add_line(get_canvas_item(),source,dest,col,2);
+ GraphCurveMapEdit *cr=p_curve->cast_to<GraphCurveMapEdit>();
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+
+
+ Vector<Point2> points=cr->get_points();
+
+ DVector<Vector2> new_points;
+ {
+ new_points.resize(points.size());
+ DVector<Vector2>::Write ow=new_points.write();
+ for(int i=0;i<new_points.size();i++) {
+ ow[i]=points[i];
+ }
- }
- } break;
}
+
+ DVector<Vector2> old_points=graph->curve_map_node_get_points(type,p_id);
+
+ if (old_points.size()!=new_points.size())
+ ur->create_action("Add/Remove to Curve Map");
+ else
+ ur->create_action("Modify Curve Map",true);
+
+ ur->add_do_method(graph.ptr(),"curve_map_node_set_points",type,p_id,new_points);
+ ur->add_undo_method(graph.ptr(),"curve_map_node_set_points",type,p_id,old_points);
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
}
-void ShaderEditor::_update_scrollbars() {
- Size2 size = get_size();
- Size2 hmin = h_scroll->get_minimum_size();
- Size2 vmin = v_scroll->get_minimum_size();
+void ShaderGraphView::_input_name_changed(const String& p_name, int p_id, Node *p_line_edit) {
- v_scroll->set_begin( Point2(size.width - vmin.width, 0) );
- v_scroll->set_end( Point2(size.width, size.height) );
+ LineEdit *le=p_line_edit->cast_to<LineEdit>();
+ ERR_FAIL_COND(!le);
- h_scroll->set_begin( Point2( 0, size.height - hmin.height) );
- h_scroll->set_end( Point2(size.width-vmin.width, size.height) );
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Change Input Name");
+ ur->add_do_method(graph.ptr(),"input_node_set_name",type,p_id,p_name);
+ ur->add_undo_method(graph.ptr(),"input_node_set_name",type,p_id,graph->input_node_get_name(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+ le->set_text(graph->input_node_get_name(type,p_id));
+}
+void ShaderGraphView::_tex_edited(int p_id,Node* p_button) {
- Size2 min = _get_maximum_size();
+ ToolButton *tb = p_button->cast_to<ToolButton>();
+ ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height));
+ ped_popup->set_size(tb->get_size());
+ edited_id=p_id;
+ edited_def=-1;
+ ped_popup->edit(NULL,"",Variant::OBJECT,graph->texture_input_node_get_value(type,p_id),PROPERTY_HINT_RESOURCE_TYPE,"Texture");
+}
- if (min.height < size.height - hmin.height) {
+void ShaderGraphView::_cube_edited(int p_id,Node* p_button) {
- v_scroll->hide();
- offset.y=0;
- } else {
+ ToolButton *tb = p_button->cast_to<ToolButton>();
+ ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height));
+ ped_popup->set_size(tb->get_size());
+ edited_id=p_id;
+ edited_def=-1;
+ ped_popup->edit(NULL,"",Variant::OBJECT,graph->cubemap_input_node_get_value(type,p_id),PROPERTY_HINT_RESOURCE_TYPE,"CubeMap");
+}
+
+
+//////////////view/////////////
+
+
+void ShaderGraphView::_connection_request(const String& p_from, int p_from_slot,const String& p_to,int p_to_slot) {
- v_scroll->show();
- v_scroll->set_max(min.height);
- v_scroll->set_page(size.height - hmin.height);
- offset.y=v_scroll->get_val();
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+
+ int from_idx=-1;
+ int to_idx=-1;
+ for (Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) {
+
+ if (p_from==E->get()->get_name())
+ from_idx=E->key();
+ if (p_to==E->get()->get_name())
+ to_idx=E->key();
}
- if (min.width < size.width - vmin.width) {
+ ERR_FAIL_COND(from_idx==-1);
+ ERR_FAIL_COND(to_idx==-1);
- h_scroll->hide();
- offset.x=0;
- } else {
+ ur->create_action("Connect Graph Nodes");
+
+ List<ShaderGraph::Connection> conns;
- h_scroll->show();
- h_scroll->set_max(min.width);
- h_scroll->set_page(size.width - vmin.width);
- offset.x=h_scroll->get_val();
+ graph->get_node_connections(type,&conns);
+ //disconnect/reconnect dependencies
+ ur->add_undo_method(graph.ptr(),"disconnect_node",type,from_idx,p_from_slot,to_idx,p_to_slot);
+ for(List<ShaderGraph::Connection>::Element *E=conns.front();E;E=E->next()) {
+
+ if (E->get().dst_id==to_idx && E->get().dst_slot==p_to_slot) {
+ ur->add_do_method(graph.ptr(),"disconnect_node",type,E->get().src_id,E->get().src_slot,E->get().dst_id,E->get().dst_slot);
+ ur->add_undo_method(graph.ptr(),"connect_node",type,E->get().src_id,E->get().src_slot,E->get().dst_id,E->get().dst_slot);
+ }
}
-}
+ ur->add_do_method(graph.ptr(),"connect_node",type,from_idx,p_from_slot,to_idx,p_to_slot);
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
-void ShaderEditor::_scroll_moved() {
- offset.x=h_scroll->get_val();
- offset.y=v_scroll->get_val();
- update();
}
-void ShaderEditor::_bind_methods() {
+void ShaderGraphView::_disconnection_request(const String& p_from, int p_from_slot,const String& p_to,int p_to_slot) {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+
+ int from_idx=-1;
+ int to_idx=-1;
+ for (Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) {
+
+ if (p_from==E->get()->get_name())
+ from_idx=E->key();
+ if (p_to==E->get()->get_name())
+ to_idx=E->key();
+ }
+
+ ERR_FAIL_COND(from_idx==-1);
+ ERR_FAIL_COND(to_idx==-1);
+
+ if (!graph->is_node_connected(type,from_idx,p_from_slot,to_idx,p_to_slot))
+ return; //nothing to disconnect
+
+ ur->create_action("Disconnect Graph Nodes");
+
+ List<ShaderGraph::Connection> conns;
+
+ graph->get_node_connections(type,&conns);
+ //disconnect/reconnect dependencies
+ ur->add_do_method(graph.ptr(),"disconnect_node",type,from_idx,p_from_slot,to_idx,p_to_slot);
+ ur->add_undo_method(graph.ptr(),"connect_node",type,from_idx,p_from_slot,to_idx,p_to_slot);
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+
- ObjectTypeDB::bind_method( "_node_menu_item", &ShaderEditor::_node_menu_item );
- ObjectTypeDB::bind_method( "_node_add_callback", &ShaderEditor::_node_add_callback );
- ObjectTypeDB::bind_method( "_input_event", &ShaderEditor::_input_event );
- ObjectTypeDB::bind_method( "_node_param_changed", &ShaderEditor::_node_param_changed );
- ObjectTypeDB::bind_method( "_scroll_moved", &ShaderEditor::_scroll_moved );
- ObjectTypeDB::bind_method( "_vertex_item", &ShaderEditor::_vertex_item );
- ObjectTypeDB::bind_method( "_fragment_item", &ShaderEditor::_fragment_item );
- ObjectTypeDB::bind_method( "_post_item", &ShaderEditor::_post_item );
}
-void ShaderEditor::_read_shader_graph() {
+void ShaderGraphView::_node_removed(int p_id) {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Remove Shader Graph Node");
- shader_graph.clear();;
- order.clear();
- List<int> nodes;
- shader->get_node_list(&nodes);
- int larger_id=0;
- for(List<int>::Element *E=nodes.front();E;E=E->next()) {
+ ur->add_do_method(graph.ptr(),"node_remove",type,p_id);
+ ur->add_undo_method(graph.ptr(),"node_add",type,graph->node_get_type(type,p_id),p_id);
+ ur->add_undo_method(graph.ptr(),"node_set_state",type,p_id,graph->node_get_state(type,p_id));
+ List<ShaderGraph::Connection> conns;
- if (E->get() > larger_id)
- larger_id = E->get();
+ graph->get_node_connections(type,&conns);
+ for(List<ShaderGraph::Connection>::Element *E=conns.front();E;E=E->next()) {
- shader_graph.node_add( (VS::ShaderNodeType)shader->node_get_type(E->get()), E->get() );
- shader_graph.node_set_param( E->get(), shader->node_get_param( E->get() ) );
- Point2 pos = shader->node_get_pos(E->get());
- shader_graph.node_set_pos( E->get(), pos.x,pos.y );
- order.push_back(E->get());
+ if (E->get().dst_id==p_id || E->get().src_id==p_id) {
+ ur->add_undo_method(graph.ptr(),"connect_node",type,E->get().src_id,E->get().src_slot,E->get().dst_id,E->get().dst_slot);
+ }
}
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
- last_id=larger_id+1;
+}
- List<Shader::Connection> connections;
- shader->get_connections(&connections);
+void ShaderGraphView::_begin_node_move()
+{
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Move Shader Graph Node");
+}
- for(List<Shader::Connection>::Element *E=connections.front();E;E=E->next()) {
+void ShaderGraphView::_node_moved(const Vector2& p_from, const Vector2& p_to,int p_id) {
- Shader::Connection &c=E->get();
- shader_graph.connect(c.src_id,c.src_slot,c.dst_id,c.dst_slot);
- }
- validate_graph();
- update();
+ ERR_FAIL_COND(!node_map.has(p_id));
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->add_do_method(this,"_move_node",p_id,p_to);
+ ur->add_undo_method(this,"_move_node",p_id,p_from);
}
-void ShaderEditor::_write_shader_graph() {
+void ShaderGraphView::_end_node_move()
+{
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->commit_action();
+}
- shader->clear();
- List<int> nodes;
- shader_graph.get_node_list(&nodes);
- for(List<int>::Element *E=nodes.front();E;E=E->next()) {
+void ShaderGraphView::_move_node(int p_id,const Vector2& p_to) {
- shader->node_add((Shader::NodeType)shader_graph.node_get_type(E->get()),E->get());
- shader->node_set_param(E->get(),shader_graph.node_get_param(E->get()));
- shader->node_set_pos(E->get(),Point2( shader_graph.node_get_pos_x(E->get()),shader_graph.node_get_pos_y(E->get()) ) );
- }
+ ERR_FAIL_COND(!node_map.has(p_id));
+ node_map[p_id]->set_offset(p_to);
+ graph->node_set_pos(type,p_id,p_to);
+}
- List<ShaderGraph::Connection> connections = shader_graph.get_connection_list();
- for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) {
+void ShaderGraphView::_duplicate_nodes_request()
+{
+ Array s_id;
- const ShaderGraph::Connection &c=E->get();
- shader->connect(c.src_id,c.src_slot,c.dst_id,c.dst_slot);
+ for(Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) {
+ ShaderGraph::NodeType t=graph->node_get_type(type, E->key());
+ if (t==ShaderGraph::NODE_OUTPUT || t==ShaderGraph::NODE_INPUT)
+ continue;
+ GraphNode *gn = E->get();
+ if (gn && gn->is_selected())
+ s_id.push_back(E->key());
}
+
+ if (s_id.size()==0)
+ return;
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Duplicate Graph Node(s)");
+ ur->add_do_method(this,"_duplicate_nodes",s_id);
+ List<int> n_ids = graph->generate_ids(type, s_id.size());
+ for (List<int>::Element *E=n_ids.front();E;E=E->next())
+ ur->add_undo_method(graph.ptr(),"node_remove",type,E->get());
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+
}
-void ShaderEditor::_add_node_from_text(const String& p_text) {
+void ShaderGraphView::_duplicate_nodes(const Array &p_nodes)
+{
+ List<int> n = List<int>();
+ for (int i=0; i<p_nodes.size();i++)
+ n.push_back(p_nodes.get(i));
+ graph->duplicate_nodes(type, n);
+ call_deferred("_update_graph");
+}
- ERR_FAIL_COND( p_text.get_slice_count(" ") != 3 );
- bool input = p_text.get_slice(" ",0)=="In:";
- String name = p_text.get_slice(" ",1);
- bool vec = p_text.get_slice(" ",2)=="(vec3)";
+void ShaderGraphView::_delete_nodes_request()
+{
+ List<int> s_id=List<int>();
- _node_add( input?
- ( vec? VisualServer::NODE_VEC_IN : VisualServer::NODE_IN ) :
- ( vec? VisualServer::NODE_VEC_OUT : VisualServer::NODE_OUT ) );
+ for(Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) {
+ ShaderGraph::NodeType t=graph->node_get_type(type, E->key());
+ if (t==ShaderGraph::NODE_OUTPUT)
+ continue;
+ GraphNode *gn = E->get();
+ if (gn && gn->is_selected())
+ s_id.push_back(E->key());
+ }
+
+ if (s_id.size()==0)
+ return;
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Delete Shader Graph Node(s)");
+
+ for (List<int>::Element *N=s_id.front();N;N=N->next()) {
+ ur->add_do_method(graph.ptr(),"node_remove",type,N->get());
+ ur->add_undo_method(graph.ptr(),"node_add",type,graph->node_get_type(type,N->get()),N->get());
+ ur->add_undo_method(graph.ptr(),"node_set_state",type,N->get(),graph->node_get_state(type,N->get()));
+ List<ShaderGraph::Connection> conns;
+
+ graph->get_node_connections(type,&conns);
+ for(List<ShaderGraph::Connection>::Element *E=conns.front();E;E=E->next()) {
+
+ if (E->get().dst_id==N->get() || E->get().src_id==N->get()) {
+ ur->add_undo_method(graph.ptr(),"connect_node",type,E->get().src_id,E->get().src_slot,E->get().dst_id,E->get().dst_slot);
+ }
+ }
+ }
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
- shader_graph.node_set_param( last_id-1,name );
- _write_shader_graph();
}
-void ShaderEditor::_vertex_item(int p_item) {
+void ShaderGraphView::_default_changed(int p_id, Node *p_button, int p_param, int v_type, String p_hint)
+{
+ ToolButton *tb = p_button->cast_to<ToolButton>();
+ ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height));
+ ped_popup->set_size(tb->get_size());
+ edited_id=p_id;
+ edited_def=p_param;
+ Variant::Type vt = (Variant::Type)v_type;
+ Variant v = graph->default_get_value(type,p_id,edited_def);
+ int h=PROPERTY_HINT_NONE;
+ if (v.get_type() == Variant::NIL)
+ switch (vt) {
+ case Variant::VECTOR3:
+ v=Vector3();
+ break;
+ case Variant::REAL:
+ h=PROPERTY_HINT_RANGE;
+ v=0.0;
+ break;
+ case Variant::TRANSFORM:
+ v=Transform();
+ break;
+ case Variant::COLOR:
+ h=PROPERTY_HINT_COLOR_NO_ALPHA;
+ v=Color();
+ break;
+ }
+
+ ped_popup->edit(NULL,"",vt,v,h,p_hint);
- _add_node_from_text(vertex_popup->get_item_text(p_item));
+ ped_popup->popup();
}
-void ShaderEditor::_fragment_item(int p_item) {
- _add_node_from_text(fragment_popup->get_item_text(p_item));
+ToolButton *ShaderGraphView::make_label(String text, Variant::Type v_type) {
+ ToolButton *l = memnew( ToolButton );
+ l->set_text(text);
+ l->set_text_align(ToolButton::ALIGN_LEFT);
+ l->add_style_override("hover", l->get_stylebox("normal", "ToolButton"));
+ l->add_style_override("pressed", l->get_stylebox("normal", "ToolButton"));
+ l->add_style_override("focus", l->get_stylebox("normal", "ToolButton"));
+ switch (v_type) {
+ case Variant::REAL:
+ l->set_icon(ped_popup->get_icon("Real", "EditorIcons"));
+ break;
+ case Variant::VECTOR3:
+ l->set_icon(ped_popup->get_icon("Vector", "EditorIcons"));
+ break;
+ case Variant::TRANSFORM:
+ l->set_icon(ped_popup->get_icon("Matrix", "EditorIcons"));
+ break;
+ case Variant::COLOR:
+ l->set_icon(ped_popup->get_icon("Color", "EditorIcons"));
+ }
+ return l;
}
-void ShaderEditor::_post_item(int p_item) {
- _add_node_from_text(post_popup->get_item_text(p_item));
+ToolButton *ShaderGraphView::make_editor(String text,GraphNode* gn,int p_id,int param,Variant::Type v_type, String p_hint) {
+ ToolButton *edit = memnew( ToolButton );
+ edit->set_text(text);
+ edit->set_text_align(ToolButton::ALIGN_LEFT);
+ edit->set_flat(false);
+ edit->add_style_override("normal", gn->get_stylebox("defaultframe", "GraphNode"));
+ edit->add_style_override("hover", gn->get_stylebox("defaultframe", "GraphNode"));
+ edit->add_style_override("pressed", gn->get_stylebox("defaultframe", "GraphNode"));
+ edit->add_style_override("focus", gn->get_stylebox("defaultfocus", "GraphNode"));
+ edit->connect("pressed",this,"_default_changed",varray(p_id,edit,param,v_type,p_hint));
+
+ switch (v_type) {
+ case Variant::REAL:
+ edit->set_icon(ped_popup->get_icon("Real", "EditorIcons"));
+ break;
+ case Variant::VECTOR3:
+ edit->set_icon(ped_popup->get_icon("Vector", "EditorIcons"));
+ break;
+ case Variant::TRANSFORM:
+ edit->set_icon(ped_popup->get_icon("Matrix", "EditorIcons"));
+ break;
+ case Variant::COLOR:
+ Image icon_color = Image(15,15,false,Image::FORMAT_RGB);
+ Color c = graph->default_get_value(type,p_id,param);
+ for (int x=1;x<14;x++)
+ for (int y=1;y<14;y++)
+ icon_color.put_pixel(x,y,c);
+ Ref<ImageTexture> t;
+ t.instance();
+ t->create_from_image(icon_color);
+ edit->set_icon(t);
+ break;
+ }
+ return edit;
}
+void ShaderGraphView::_create_node(int p_id) {
-void ShaderEditor::_node_menu_item(int p_item) {
- switch(p_item) {
+ GraphNode *gn = memnew( GraphNode );
+ gn->set_show_close_button(true);
+ Color typecol[4]={
+ Color(0.9,0.4,1),
+ Color(0.8,1,0.2),
+ Color(1,0.2,0.2),
+ Color(0,1,1)
+ };
- case GRAPH_ADD_NODE: {
- add_popup->popup_centered_ratio();
- validate_graph();
- } break;
- case NODE_DISCONNECT: {
+ const String hint_spin = "-65536,65535,0.001";
+ const String hint_slider = "0.0,1.0,0.01,slider";
- if (rclick_type==CLICK_INPUT_SLOT) {
- List<ShaderGraph::Connection> connections = shader_graph.get_connection_list();
- for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) {
+ switch(graph->node_get_type(type,p_id)) {
- const ShaderGraph::Connection &c=E->get();
- if( c.dst_id==rclick_node && c.dst_slot==rclick_slot) {
+ case ShaderGraph::NODE_INPUT: {
- shader_graph.disconnect(c.src_id,c.src_slot,c.dst_id,c.dst_slot);
- }
- }
- update();
- _write_shader_graph();
- validate_graph();
+ gn->set_title("Input");
+
+ List<ShaderGraph::SlotInfo> si;
+ ShaderGraph::get_input_output_node_slot_info(graph->get_mode(),type,&si);
+
+ int idx=0;
+ for (List<ShaderGraph::SlotInfo>::Element *E=si.front();E;E=E->next()) {
+ ShaderGraph::SlotInfo& s=E->get();
+ if (s.dir==ShaderGraph::SLOT_IN) {
+
+ Label *l= memnew( Label );
+ l->set_text(s.name);
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+ gn->set_slot(idx,false,0,Color(),true,s.type,typecol[s.type]);
+ idx++;
}
+ }
- if (rclick_type==CLICK_OUTPUT_SLOT) {
+ } break; // all inputs (case Shader type dependent)
+ case ShaderGraph::NODE_SCALAR_CONST: {
+ gn->set_title("Scalar");
+ SpinBox *sb = memnew( SpinBox );
+ sb->set_min(-100000);
+ sb->set_max(100000);
+ sb->set_step(0.001);
+ sb->set_val(graph->scalar_const_node_get_value(type,p_id));
+ sb->connect("value_changed",this,"_scalar_const_changed",varray(p_id));
+ gn->add_child(sb);
+ gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; //scalar constant
+ case ShaderGraph::NODE_VEC_CONST: {
+
+ gn->set_title("Vector");
+ Array v3p(true);
+ for(int i=0;i<3;i++) {
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ Label *l = memnew( Label );
+ l->set_text(String::chr('X'+i));
+ hbc->add_child(l);
+ SpinBox *sb = memnew( SpinBox );
+ sb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ sb->set_min(-100000);
+ sb->set_max(100000);
+ sb->set_step(0.001);
+ sb->set_val(graph->vec_const_node_get_value(type,p_id)[i]);
+ sb->connect("value_changed",this,"_vec_const_changed",varray(p_id,v3p));
+ v3p.push_back(sb);
+ hbc->add_child(sb);
+ gn->add_child(hbc);
+ }
+ gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+
+ } break; //vec3 constant
+ case ShaderGraph::NODE_RGB_CONST: {
+
+ gn->set_title("Color");
+ ColorPickerButton *cpb = memnew( ColorPickerButton );
+ cpb->set_color(graph->rgb_const_node_get_value(type,p_id));
+ cpb->connect("color_changed",this,"_rgb_const_changed",varray(p_id));
+ gn->add_child(cpb);
+ Label *l = memnew( Label );
+ l->set_text("RGB");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+ l = memnew( Label );
+ l->set_text("Alpha");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; //rgb constant (shows a color picker instead)
+ case ShaderGraph::NODE_XFORM_CONST: {
+ gn->set_title("XForm");
+ ToolButton *edit = memnew( ToolButton );
+ edit->set_text("edit..");
+ edit->connect("pressed",this,"_xform_const_changed",varray(p_id,edit));
+ gn->add_child(edit);
+ gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]);
+
+ } break; // 4x4 matrix constant
+ case ShaderGraph::NODE_TIME: {
+
+ gn->set_title("Time");
+ Label *l = memnew( Label );
+ l->set_text("(s)");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+ gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; // time in seconds
+ case ShaderGraph::NODE_SCREEN_TEX: {
+
+ gn->set_title("ScreenTex");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (!graph->is_slot_connected(type,p_id,0)) {
+ Vector3 v = graph->default_get_value(type, p_id, 0);
+ hbc->add_child(make_editor("UV: " + v,gn,p_id,0,Variant::VECTOR3));
+ } else {
+ hbc->add_child(make_label("UV",Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("RGB")));
+ gn->add_child(hbc);
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+
+ } break; // screen texture sampler (takes UV) (only usable in fragment case Shader)
+ case ShaderGraph::NODE_SCALAR_OP: {
+
+ gn->set_title("ScalarOp");
+ static const char* op_name[ShaderGraph::SCALAR_MAX_OP]={
+ "Add",
+ "Sub",
+ "Mul",
+ "Div",
+ "Mod",
+ "Pow",
+ "Max",
+ "Min",
+ "Atan2"
+ };
+
+ OptionButton *ob = memnew( OptionButton );
+ for(int i=0;i<ShaderGraph::SCALAR_MAX_OP;i++) {
+
+ ob->add_item(op_name[i],i);
+ }
- List<ShaderGraph::Connection> connections = shader_graph.get_connection_list();
- for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) {
+ ob->select(graph->scalar_op_node_get_op(type,p_id));
+ ob->connect("item_selected",this,"_scalar_op_changed",varray(p_id));
+ gn->add_child(ob);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a",Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("a: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_spin));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b",Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("b: ")+Variant(v),gn,p_id,1,Variant::REAL,hint_spin));
+ }
- const ShaderGraph::Connection &c=E->get();
- if( c.src_id==rclick_node && c.src_slot==rclick_slot) {
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
- shader_graph.disconnect(c.src_id,c.src_slot,c.dst_id,c.dst_slot);
- }
- }
- update();
- _write_shader_graph();
- validate_graph();
+
+ } break; // scalar vs scalar op (mul: { } break; add: { } break; div: { } break; etc)
+ case ShaderGraph::NODE_VEC_OP: {
+
+ gn->set_title("VecOp");
+ static const char* op_name[ShaderGraph::VEC_MAX_OP]={
+ "Add",
+ "Sub",
+ "Mul",
+ "Div",
+ "Mod",
+ "Pow",
+ "Max",
+ "Min",
+ "Cross"
+ };
+
+ OptionButton *ob = memnew( OptionButton );
+ for(int i=0;i<ShaderGraph::VEC_MAX_OP;i++) {
+
+ ob->add_item(op_name[i],i);
+ }
+
+ ob->select(graph->vec_op_node_get_op(type,p_id));
+ ob->connect("item_selected",this,"_vec_op_changed",varray(p_id));
+ gn->add_child(ob);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a",Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b",Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("b: ")+v,gn,p_id,1,Variant::VECTOR3));
+ }
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+
+
+ } break; // vec3 vs vec3 op (mul: { } break;ad: { } break;div: { } break;crossprod: { } break;etc)
+ case ShaderGraph::NODE_VEC_SCALAR_OP: {
+
+ gn->set_title("VecScalarOp");
+ static const char* op_name[ShaderGraph::VEC_SCALAR_MAX_OP]={
+ "Mul",
+ "Div",
+ "Pow",
+ };
+
+ OptionButton *ob = memnew( OptionButton );
+ for(int i=0;i<ShaderGraph::VEC_SCALAR_MAX_OP;i++) {
+
+ ob->add_item(op_name[i],i);
+ }
+
+ ob->select(graph->vec_scalar_op_node_get_op(type,p_id));
+ ob->connect("item_selected",this,"_vec_scalar_op_changed",varray(p_id));
+ gn->add_child(ob);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a",Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b",Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("b: ")+Variant(v),gn,p_id,1,Variant::REAL,hint_spin));
+ }
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
+
+
+ } break; // vec3 vs scalar op (mul: { } break; add: { } break; div: { } break; etc)
+ case ShaderGraph::NODE_RGB_OP: {
+
+ gn->set_title("RGB Op");
+ static const char* op_name[ShaderGraph::RGB_MAX_OP]={
+ "Screen",
+ "Difference",
+ "Darken",
+ "Lighten",
+ "Overlay",
+ "Dodge",
+ "Burn",
+ "SoftLight",
+ "HardLight"
+ };
+
+ OptionButton *ob = memnew( OptionButton );
+ for(int i=0;i<ShaderGraph::RGB_MAX_OP;i++) {
+
+ ob->add_item(op_name[i],i);
+ }
+
+ ob->select(graph->rgb_op_node_get_op(type,p_id));
+ ob->connect("item_selected",this,"_rgb_op_changed",varray(p_id));
+ gn->add_child(ob);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a",Variant::COLOR));
+ } else {
+ hbc->add_child(make_editor(String("a: "),gn,p_id,0,Variant::COLOR));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b",Variant::COLOR));
+ } else {
+ gn->add_child(make_editor(String("b: "),gn,p_id,1,Variant::COLOR));
+ }
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+
+ } break; // vec3 vs vec3 rgb op (with scalar amount): { } break; like brighten: { } break; darken: { } break; burn: { } break; dodge: { } break; multiply: { } break; etc.
+ case ShaderGraph::NODE_XFORM_MULT: {
+
+ gn->set_title("XFMult");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a",Variant::TRANSFORM));
+ } else {
+ hbc->add_child(make_editor(String("a: edit..."),gn,p_id,0,Variant::TRANSFORM));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b",Variant::TRANSFORM));
+ } else {
+ gn->add_child(make_editor(String("b: edit..."),gn,p_id,1,Variant::TRANSFORM));
+ }
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]);
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],false,0,Color());
+
+
+ } break; // mat4 x mat4
+ case ShaderGraph::NODE_XFORM_VEC_MULT: {
+
+ gn->set_title("XFVecMult");
+
+ CheckBox *button = memnew (CheckBox("RotOnly"));
+ button->set_pressed(graph->xform_vec_mult_node_get_no_translation(type,p_id));
+ button->connect("toggled",this,"_xform_inv_rev_changed",varray(p_id));
+
+ gn->add_child(button);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("xf",Variant::TRANSFORM));
+ } else {
+ hbc->add_child(make_editor(String("xf: edit..."),gn,p_id,0,Variant::TRANSFORM));
+ }
+ hbc->add_spacer();
+ Label *l = memnew(Label("out"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child( l);
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("a",Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("a: ")+v,gn,p_id,1,Variant::VECTOR3));
+ }
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+
+ } break;
+ case ShaderGraph::NODE_XFORM_VEC_INV_MULT: {
+
+ gn->set_title("XFVecInvMult");
+
+
+ CheckBox *button = memnew( CheckBox("RotOnly"));
+ button->set_pressed(graph->xform_vec_mult_node_get_no_translation(type,p_id));
+ button->connect("toggled",this,"_xform_inv_rev_changed",varray(p_id));
+
+ gn->add_child(button);
+
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ gn->add_child(make_label("a",Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ gn->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ hbc->add_child(make_label("xf", Variant::TRANSFORM));
+ } else {
+ hbc->add_child(make_editor(String("xf: edit..."),gn,p_id,1,Variant::TRANSFORM));
+ }
+ hbc->add_spacer();
+ Label *l = memnew(Label("out"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child( l);
+ gn->add_child(hbc);
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+
+
+ } break; // mat4 x vec3 inverse mult (with no-translation option)
+ case ShaderGraph::NODE_SCALAR_FUNC: {
+
+ gn->set_title("ScalarFunc");
+ static const char* func_name[ShaderGraph::SCALAR_MAX_FUNC]={
+ "Sin",
+ "Cos",
+ "Tan",
+ "ASin",
+ "ACos",
+ "ATan",
+ "SinH",
+ "CosH",
+ "TanH",
+ "Log",
+ "Exp",
+ "Sqrt",
+ "Abs",
+ "Sign",
+ "Floor",
+ "Round",
+ "Ceil",
+ "Frac",
+ "Satr",
+ "Neg"
+ };
+
+ OptionButton *ob = memnew( OptionButton );
+ for(int i=0;i<ShaderGraph::SCALAR_MAX_FUNC;i++) {
+
+ ob->add_item(func_name[i],i);
+ }
+
+ ob->select(graph->scalar_func_node_get_function(type,p_id));
+ ob->connect("item_selected",this,"_scalar_func_changed",varray(p_id));
+ gn->add_child(ob);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("in", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("in: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_spin));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+
+ } break; // scalar function (sin: { } break; cos: { } break; etc)
+ case ShaderGraph::NODE_VEC_FUNC: {
+
+
+
+ gn->set_title("VecFunc");
+ static const char* func_name[ShaderGraph::VEC_MAX_FUNC]={
+ "Normalize",
+ "Saturate",
+ "Negate",
+ "Reciprocal",
+ "RGB to HSV",
+ "HSV to RGB",
+ };
+
+ OptionButton *ob = memnew( OptionButton );
+ for(int i=0;i<ShaderGraph::VEC_MAX_FUNC;i++) {
+
+ ob->add_item(func_name[i],i);
+ }
+
+ ob->select(graph->vec_func_node_get_function(type,p_id));
+ ob->connect("item_selected",this,"_vec_func_changed",varray(p_id));
+ gn->add_child(ob);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("in", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("in: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+
+ } break; // vector function (normalize: { } break; negate: { } break; reciprocal: { } break; rgb2hsv: { } break; hsv2rgb: { } break; etc: { } break; etc)
+ case ShaderGraph::NODE_VEC_LEN: {
+ gn->set_title("VecLength");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("in", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("in: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("len")));
+ gn->add_child(hbc);
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; // vec3 length
+ case ShaderGraph::NODE_DOT_PROD: {
+
+ gn->set_title("DotProduct");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("dp")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("b: ")+v,gn,p_id,1,Variant::VECTOR3));
+ }
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+
+ } break; // vec3 . vec3 (dot product -> scalar output)
+ case ShaderGraph::NODE_VEC_TO_SCALAR: {
+
+ gn->set_title("Vec2Scalar");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("vec", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("vec: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("x"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child( l);
+ gn->add_child(hbc);
+ l=memnew(Label("y"));
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child( l );
+ l=memnew(Label("z"));
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child( l);
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+ gn->set_slot(2,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+
+
+
+ } break; // 1 vec3 input: { } break; 3 scalar outputs
+ case ShaderGraph::NODE_SCALAR_TO_VEC: {
+
+ gn->set_title("Scalar2Vec");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("x", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("x: ")+Variant(v),gn,p_id,0,Variant::REAL));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("vec")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("y", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("y: ")+Variant(v),gn,p_id,1,Variant::REAL));
+ }
+ if (graph->is_slot_connected(type, p_id, 2)) {
+ gn->add_child(make_label("in", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,2);
+ gn->add_child(make_editor(String("in: ")+Variant(v),gn,p_id,2,Variant::REAL));
+ }
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
+
+ } break; // 3 scalar input: { } break; 1 vec3 output
+ case ShaderGraph::NODE_VEC_TO_XFORM: {
+
+ gn->set_title("Vec2XForm");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("x", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("x: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("xf")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("y", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("y: ")+v,gn,p_id,1,Variant::VECTOR3));
+ }
+ if (graph->is_slot_connected(type, p_id, 2)) {
+ gn->add_child(make_label("z", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,2);
+ gn->add_child(make_editor(String("z: ")+v,gn,p_id,2,Variant::VECTOR3));
+ }
+ if (graph->is_slot_connected(type, p_id, 3)) {
+ gn->add_child(make_label("ofs", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,3);
+ gn->add_child(make_editor(String("ofs: ")+v,gn,p_id,3,Variant::VECTOR3));
+ }
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]);
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+ gn->set_slot(3,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+
+ } break; // 3 vec input: { } break; 1 xform output
+ case ShaderGraph::NODE_XFORM_TO_VEC: {
+
+ gn->set_title("XForm2Vec");
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("fx", Variant::TRANSFORM));
+ } else {
+ hbc->add_child(make_editor(String("fx: edit..."),gn,p_id,0,Variant::TRANSFORM));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("x"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child( l);
+ gn->add_child(hbc);
+ l=memnew(Label("y"));
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child( l );
+ l=memnew(Label("z"));
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child( l);
+ l=memnew(Label("ofs"));
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child( l);
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(3,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+
+ } break; // 3 vec input: { } break; 1 xform output
+ case ShaderGraph::NODE_SCALAR_INTERP: {
+
+ gn->set_title("ScalarInterp");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("a: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_spin));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("interp")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("b: ")+Variant(v),gn,p_id,1,Variant::REAL,hint_spin));
+ }
+ if (graph->is_slot_connected(type, p_id, 2)) {
+ gn->add_child(make_label("c", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,2);
+ gn->add_child(make_editor(String("c: ")+Variant(v),gn,p_id,2,Variant::REAL,hint_slider));
+ }
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
+
+
+ } break; // scalar interpolation (with optional curve)
+ case ShaderGraph::NODE_VEC_INTERP: {
+
+ gn->set_title("VecInterp");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("interp")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("b: ")+v,gn,p_id,1,Variant::VECTOR3));
+ }
+ if (graph->is_slot_connected(type, p_id, 2)) {
+ gn->add_child(make_label("c", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,2);
+ gn->add_child(make_editor(String("c: ")+Variant(v),gn,p_id,2,Variant::REAL,hint_slider));
+ }
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
+
+ } break; // vec3 interpolation (with optional curve)
+ case ShaderGraph::NODE_COLOR_RAMP: {
+
+ gn->set_title("ColorRamp");
+ GraphColorRampEdit * ramp = memnew( GraphColorRampEdit );
+
+ DVector<real_t> offsets = graph->color_ramp_node_get_offsets(type,p_id);
+ DVector<Color> colors = graph->color_ramp_node_get_colors(type,p_id);
+
+ int oc = offsets.size();
+
+ if (oc) {
+ DVector<real_t>::Read rofs = offsets.read();
+ DVector<Color>::Read rcol = colors.read();
+
+ Vector<float> ofsv;
+ Vector<Color> colorv;
+ for(int i=0;i<oc;i++) {
+ ofsv.push_back(rofs[i]);
+ colorv.push_back(rcol[i]);
}
- } break;
- case NODE_ERASE: {
-
- order.erase(rclick_node);
- shader_graph.node_remove(rclick_node);
- update();
- _write_shader_graph();
- validate_graph();
- } break;
- case GRAPH_CLEAR: {
-
- order.clear();
- shader_graph.clear();
- last_id=1;
- last_x=20;
- last_y=20;
- update();
- _write_shader_graph();
- validate_graph();
-
- } break;
- }
-}
+ ramp->set_ramp(ofsv,colorv);
-void ShaderEditor::_node_add(VisualServer::ShaderNodeType p_type) {
+ }
- shader_graph.node_add(p_type,last_id );
- shader_graph.node_set_pos(last_id ,last_x,last_y);
- String test_param;
+ ramp->connect("ramp_changed",this,"_color_ramp_changed",varray(p_id,ramp));
+ ramp->set_custom_minimum_size(Size2(128,1));
+ gn->add_child(ramp);
- switch(p_type) {
- case VS::NODE_PARAMETER: {
- test_param="param";
- } break;
- case VS::NODE_VEC_PARAMETER: {
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("c", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("c: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_slider));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("rgb"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child( l);
+ gn->add_child(hbc);
+ l=memnew(Label("alpha"));
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child( l);
- test_param="vec";
- } break;
- case VS::NODE_COLOR_PARAMETER: {
- test_param="color";
- } break;
- case VS::NODE_TEXTURE_PARAMETER: {
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,false,ShaderGraph::SLOT_MAX,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
- test_param="tex";
- } break;
- case VS::NODE_TEXTURE_2D_PARAMETER: {
- test_param="tex2D";
- } break;
- case VS::NODE_TEXTURE_CUBE_PARAMETER: {
+ } break; // scalar interpolation (with optional curve)
+ case ShaderGraph::NODE_CURVE_MAP: {
- test_param="cubemap";
- } break;
- case VS::NODE_TRANSFORM_PARAMETER: {
- test_param="xform";
- } break;
- case VS::NODE_LABEL: {
+ gn->set_title("CurveMap");
+ GraphCurveMapEdit * map = memnew( GraphCurveMapEdit );
- test_param="label";
- } break;
- }
+ DVector<Vector2> points = graph->curve_map_node_get_points(type,p_id);
- if(test_param!="") {
+ int oc = points.size();
- int iter=0;
- List<int> l;
+ if (oc) {
+ DVector<Vector2>::Read rofs = points.read();
- shader_graph.get_node_list(&l);
- bool found;
- String test;
- do {
- iter++;
- test=test_param;
- if (iter>1)
- test+="_"+itos(iter);
- found=false;
- for(List<int>::Element *E=l.front();E;E=E->next()) {
+ Vector<Vector2> ofsv;
+ for(int i=0;i<oc;i++) {
+ ofsv.push_back(rofs[i]);
+ }
+ map->set_points(ofsv);
- String param = shader_graph.node_get_param( E->get() );
- if (param==test) {
- found=true;
- break;
- }
+ }
+ map->connect("curve_changed",this,"_curve_changed",varray(p_id,map));
+
+ //map->connect("map_changed",this,"_curve_map_changed",varray(p_id,map));
+ map->set_custom_minimum_size(Size2(128,64));
+ gn->add_child(map);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("c", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("c: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_slider));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("cmap"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child( l);
+ gn->add_child(hbc);
+
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+
+ } break; // scalar interpolation (with optional curve)
+
+ case ShaderGraph::NODE_SCALAR_INPUT: {
+
+ gn->set_title("ScalarUniform");
+ LineEdit *le = memnew( LineEdit );
+ gn->add_child(le);
+ le->set_text(graph->input_node_get_name(type,p_id));
+ le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
+ SpinBox *sb = memnew( SpinBox );
+ sb->set_min(-100000);
+ sb->set_max(100000);
+ sb->set_step(0.001);
+ sb->set_val(graph->scalar_input_node_get_value(type,p_id));
+ sb->connect("value_changed",this,"_scalar_input_changed",varray(p_id));
+ gn->add_child(sb);
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; // scalar uniform (assignable in material)
+ case ShaderGraph::NODE_VEC_INPUT: {
+
+ gn->set_title("VectorUniform");
+ LineEdit *le = memnew( LineEdit );
+ gn->add_child(le);
+ le->set_text(graph->input_node_get_name(type,p_id));
+ le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
+ Array v3p(true);
+ for(int i=0;i<3;i++) {
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ Label *l = memnew( Label );
+ l->set_text(String::chr('X'+i));
+ hbc->add_child(l);
+ SpinBox *sb = memnew( SpinBox );
+ sb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ sb->set_min(-100000);
+ sb->set_max(100000);
+ sb->set_step(0.001);
+ sb->set_val(graph->vec_input_node_get_value(type,p_id)[i]);
+ sb->connect("value_changed",this,"_vec_input_changed",varray(p_id,v3p));
+ v3p.push_back(sb);
+ hbc->add_child(sb);
+ gn->add_child(hbc);
+ }
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+
+ } break; // vec3 uniform (assignable in material)
+ case ShaderGraph::NODE_RGB_INPUT: {
+
+ gn->set_title("ColorUniform");
+ LineEdit *le = memnew( LineEdit );
+ gn->add_child(le);
+ le->set_text(graph->input_node_get_name(type,p_id));
+ le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
+ ColorPickerButton *cpb = memnew( ColorPickerButton );
+ cpb->set_color(graph->rgb_input_node_get_value(type,p_id));
+ cpb->connect("color_changed",this,"_rgb_input_changed",varray(p_id));
+ gn->add_child(cpb);
+ Label *l = memnew( Label );
+ l->set_text("RGB");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+ l = memnew( Label );
+ l->set_text("Alpha");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+
+ gn->set_slot(2,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(3,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+
+ } break; // color uniform (assignable in material)
+ case ShaderGraph::NODE_XFORM_INPUT: {
+ gn->set_title("XFUniform");
+ LineEdit *le = memnew( LineEdit );
+ gn->add_child(le);
+ le->set_text(graph->input_node_get_name(type,p_id));
+ le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
+ ToolButton *edit = memnew( ToolButton );
+ edit->set_text("edit..");
+ edit->connect("pressed",this,"_xform_input_changed",varray(p_id,edit));
+ gn->add_child(edit);
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]);
+
+ } break; // mat4 uniform (assignable in material)
+ case ShaderGraph::NODE_TEXTURE_INPUT: {
+
+ gn->set_title("TexUniform");
+ LineEdit *le = memnew( LineEdit );
+ gn->add_child(le);
+ le->set_text(graph->input_node_get_name(type,p_id));
+ le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
+ TextureFrame *tex = memnew( TextureFrame );
+ tex->set_expand(true);
+ tex->set_custom_minimum_size(Size2(80,80));
+ gn->add_child(tex);
+ tex->set_texture(graph->texture_input_node_get_value(type,p_id));
+ ToolButton *edit = memnew( ToolButton );
+ edit->set_text("edit..");
+ edit->connect("pressed",this,"_tex_edited",varray(p_id,edit));
+ gn->add_child(edit);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("UV", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("UV: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("RGB"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child(l);
+ gn->add_child(hbc);
+ l = memnew( Label );
+ l->set_text("Alpha");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+
+ gn->set_slot(3,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(4,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; // texture input (assignable in material)
+ case ShaderGraph::NODE_CUBEMAP_INPUT: {
+
+ gn->set_title("TexUniform");
+ LineEdit *le = memnew( LineEdit );
+ gn->add_child(le);
+ le->set_text(graph->input_node_get_name(type,p_id));
+ le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
+
+ ToolButton *edit = memnew( ToolButton );
+ edit->set_text("edit..");
+ edit->connect("pressed",this,"_cube_edited",varray(p_id,edit));
+ gn->add_child(edit);
+
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("UV", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("UV: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("RGB"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child(l);
+ gn->add_child(hbc);
+ l = memnew( Label );
+ l->set_text("Alpha");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(3,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; // cubemap input (assignable in material)
+ case ShaderGraph::NODE_DEFAULT_TEXTURE: {
+
+ gn->set_title("CanvasItemTex");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("UV", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("UV: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("RGB"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child(l);
+ gn->add_child(hbc);
+ l = memnew( Label );
+ l->set_text("Alpha");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+
+ } break; // screen texture sampler (takes UV) (only usable in fragment case Shader)
+
+ case ShaderGraph::NODE_OUTPUT: {
+ gn->set_title("Output");
+ gn->set_show_close_button(false);
+
+ List<ShaderGraph::SlotInfo> si;
+ ShaderGraph::get_input_output_node_slot_info(graph->get_mode(),type,&si);
+
+ Array colors;
+ colors.push_back("Color");
+ colors.push_back("LightColor");
+ colors.push_back("Light");
+ colors.push_back("Diffuse");
+ colors.push_back("Specular");
+ colors.push_back("Emmision");
+ Array reals;
+ reals.push_back("Alpha");
+ reals.push_back("DiffuseAlpha");
+ reals.push_back("NormalMapDepth");
+ reals.push_back("SpecExp");
+ reals.push_back("Glow");
+ reals.push_back("ShadeParam");
+ reals.push_back("SpecularExp");
+ reals.push_back("LightAlpha");
+ reals.push_back("PointSize");
+ reals.push_back("Discard");
+
+ int idx=0;
+ for (List<ShaderGraph::SlotInfo>::Element *E=si.front();E;E=E->next()) {
+ ShaderGraph::SlotInfo& s=E->get();
+ if (s.dir==ShaderGraph::SLOT_OUT) {
+ Variant::Type v;
+ if (colors.find(s.name)>=0)
+ v=Variant::COLOR;
+ else if (reals.find(s.name)>=0)
+ v=Variant::REAL;
+ else
+ v=Variant::VECTOR3;
+ gn->add_child(make_label(s.name, v));
+ gn->set_slot(idx,true,s.type,typecol[s.type],false,0,Color());
+ idx++;
}
+ }
- } while (found);
+ } break; // output (case Shader type dependent)
+ case ShaderGraph::NODE_COMMENT: {
+ gn->set_title("Comment");
+ TextEdit *te = memnew(TextEdit);
+ te->set_custom_minimum_size(Size2(100,100));
+ gn->add_child(te);
+ te->set_text(graph->comment_node_get_text(type,p_id));
+ te->connect("text_changed",this,"_comment_edited",varray(p_id,te));
+
+ } break; // comment
- shader_graph.node_set_param(last_id,test);
}
- order.push_back(last_id);
- last_x+=10;
- last_y+=10;
- last_id++;
- last_x=last_x % (int)get_size().width;
- last_y=last_y % (int)get_size().height;
- update();
- add_popup->hide();;
- _write_shader_graph();
-}
+ gn->connect("dragged",this,"_node_moved",varray(p_id));
+ gn->connect("close_request",this,"_node_removed",varray(p_id),CONNECT_DEFERRED);
+ graph_edit->add_child(gn);
+ node_map[p_id]=gn;
+ gn->set_offset(graph->node_get_pos(type,p_id));
-void ShaderEditor::_node_add_callback() {
- TreeItem * item = add_types->get_selected();
- ERR_FAIL_COND(!item);
- _node_add((VisualServer::ShaderNodeType)(int)item->get_metadata(0));
- add_popup->hide() ;
}
-ShaderEditor::ShaderEditor() {
+void ShaderGraphView::_update_graph() {
- set_focus_mode(FOCUS_ALL);
- Panel* menu_panel = memnew( Panel );
- menu_panel->set_anchor( MARGIN_RIGHT, Control::ANCHOR_END );
- menu_panel->set_end( Point2(0,22) );
+ if (block_update)
+ return;
+
+ for (Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) {
- add_child( menu_panel );
+ memdelete(E->get());
+ }
- PopupMenu *p;
- List<PropertyInfo> defaults;
+ node_map.clear();
- MenuButton* node_menu = memnew( MenuButton );
- node_menu->set_text("Graph");
- node_menu->set_pos( Point2( 5,0) );
- menu_panel->add_child( node_menu );
+ if (!graph.is_valid())
+ return;
- p=node_menu->get_popup();
- p->add_item("Add Node",GRAPH_ADD_NODE);
- p->add_separator();
- p->add_item("Clear",GRAPH_CLEAR);
- p->connect("item_pressed", this,"_node_menu_item");
- MenuButton* vertex_menu = memnew( MenuButton );
- vertex_menu->set_text("Vertex");
- vertex_menu->set_pos( Point2( 49,0) );
- menu_panel->add_child( vertex_menu );
+ List<int> nl;
+ graph->get_node_list(type,&nl);
- p=vertex_menu->get_popup();
- defaults.clear();
- VisualServer::shader_get_default_input_nodes(VisualServer::SHADER_VERTEX,&defaults);
+ for(List<int>::Element *E=nl.front();E;E=E->next()) {
- int id=0;
- for(int i=0;i<defaults.size();i++) {
+ _create_node(E->get());
+ }
+ graph_edit->clear_connections();
- p->add_item("In: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++);
+ List<ShaderGraph::Connection> connections;
+ graph->get_node_connections(type,&connections);
+ for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) {
+
+ ERR_CONTINUE(!node_map.has(E->get().src_id) || !node_map.has(E->get().dst_id));
+ graph_edit->connect_node(node_map[E->get().src_id]->get_name(),E->get().src_slot,node_map[E->get().dst_id]->get_name(),E->get().dst_slot);
}
- p->add_separator();
- id++;
- defaults.clear();
- VisualServer::shader_get_default_output_nodes(VisualServer::SHADER_VERTEX,&defaults);
+}
- for(int i=0;i<defaults.size();i++) {
+void ShaderGraphView::_sg_updated() {
- p->add_item("Out: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++);
+ if (!graph.is_valid())
+ return;
+ switch(graph->get_graph_error(type)) {
+ case ShaderGraph::GRAPH_OK: status->set_text(""); break;
+ case ShaderGraph::GRAPH_ERROR_CYCLIC: status->set_text("Error: Cyclic Connection Link"); break;
+ case ShaderGraph::GRAPH_ERROR_MISSING_CONNECTIONS: status->set_text("Error: Missing Input Connections"); break;
}
+}
- vertex_popup=p;
- vertex_popup->connect("item_pressed", this,"_vertex_item");
- MenuButton* fragment_menu = memnew( MenuButton );
- fragment_menu->set_text("Fragment");
- fragment_menu->set_pos( Point2( 95 ,0) );
- menu_panel->add_child( fragment_menu );
+void ShaderGraphView::set_graph(Ref<ShaderGraph> p_graph){
- p=fragment_menu->get_popup();
- defaults.clear();
- VisualServer::shader_get_default_input_nodes(VisualServer::SHADER_FRAGMENT,&defaults);
- id=0;
- for(int i=0;i<defaults.size();i++) {
- p->add_item("In: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++);
+ if (graph.is_valid()) {
+ graph->disconnect("updated",this,"_sg_updated");
+ }
+ graph=p_graph;
+ if (graph.is_valid()) {
+ graph->connect("updated",this,"_sg_updated");
}
- p->add_separator();
- id++;
- defaults.clear();
- VisualServer::shader_get_default_output_nodes(VisualServer::SHADER_FRAGMENT,&defaults);
+ _update_graph();
+ _sg_updated();
+
+}
- for(int i=0;i<defaults.size();i++) {
+void ShaderGraphView::_notification(int p_what) {
- p->add_item("Out: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++);
- }
+ if (p_what==NOTIFICATION_ENTER_TREE) {
- fragment_popup=p;
- fragment_popup->connect("item_pressed", this,"_fragment_item");
+ ped_popup->connect("variant_changed",this,"_variant_edited");
+ }
+}
- MenuButton* post_menu = memnew( MenuButton );
- post_menu->set_text("Post");
- post_menu->set_pos( Point2( 161,0) );
- menu_panel->add_child( post_menu );
+void ShaderGraphView::add_node(int p_type, const Vector2 &location) {
- p=post_menu->get_popup();
- defaults.clear();
- VisualServer::shader_get_default_input_nodes(VisualServer::SHADER_POST_PROCESS,&defaults);
- id=0;
- for(int i=0;i<defaults.size();i++) {
+ if (p_type==ShaderGraph::NODE_INPUT && graph->node_count(type, p_type)>0)
+ return;
- p->add_item("In: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++);
+ List<int> existing;
+ graph->get_node_list(type,&existing);
+ existing.sort();
+ int newid=1;
+ for(List<int>::Element *E=existing.front();E;E=E->next()) {
+ if (!E->next() || (E->get()+1!=E->next()->get())){
+ newid=E->get()+1;
+ break;
+ }
}
- p->add_separator();
- id++;
- defaults.clear();
- VisualServer::shader_get_default_output_nodes(VisualServer::SHADER_POST_PROCESS,&defaults);
+ Vector2 init_ofs = location;
+ while(true) {
+ bool valid=true;
+ for(List<int>::Element *E=existing.front();E;E=E->next()) {
+ Vector2 pos = graph->node_get_pos(type,E->get());
+ if (init_ofs==pos) {
+ init_ofs+=Vector2(20,20);
+ valid=false;
+ break;
- for(int i=0;i<defaults.size();i++) {
+ }
+ }
- p->add_item("Out: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++);
+ if (valid)
+ break;
}
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Add Shader Graph Node");
+ ur->add_do_method(graph.ptr(),"node_add",type,p_type,newid);
+ ur->add_do_method(graph.ptr(),"node_set_pos",type,newid,init_ofs);
+ ur->add_undo_method(graph.ptr(),"node_remove",type,newid);
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
- post_popup=p;
- post_popup->connect("item_pressed", this,"_post_item");
+}
+void ShaderGraphView::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_update_graph",&ShaderGraphView::_update_graph);
+ ObjectTypeDB::bind_method("_begin_node_move", &ShaderGraphView::_begin_node_move);
+ ObjectTypeDB::bind_method("_node_moved",&ShaderGraphView::_node_moved);
+ ObjectTypeDB::bind_method("_end_node_move", &ShaderGraphView::_end_node_move);
+ ObjectTypeDB::bind_method("_move_node",&ShaderGraphView::_move_node);
+ ObjectTypeDB::bind_method("_node_removed",&ShaderGraphView::_node_removed);
+ ObjectTypeDB::bind_method("_connection_request",&ShaderGraphView::_connection_request);
+ ObjectTypeDB::bind_method("_disconnection_request",&ShaderGraphView::_disconnection_request);
+ ObjectTypeDB::bind_method("_duplicate_nodes_request", &ShaderGraphView::_duplicate_nodes_request);
+ ObjectTypeDB::bind_method("_duplicate_nodes", &ShaderGraphView::_duplicate_nodes);
+ ObjectTypeDB::bind_method("_delete_nodes_request", &ShaderGraphView::_delete_nodes_request);
+
+ ObjectTypeDB::bind_method("_default_changed",&ShaderGraphView::_default_changed);
+ ObjectTypeDB::bind_method("_scalar_const_changed",&ShaderGraphView::_scalar_const_changed);
+ ObjectTypeDB::bind_method("_vec_const_changed",&ShaderGraphView::_vec_const_changed);
+ ObjectTypeDB::bind_method("_rgb_const_changed",&ShaderGraphView::_rgb_const_changed);
+ ObjectTypeDB::bind_method("_xform_const_changed",&ShaderGraphView::_xform_const_changed);
+ ObjectTypeDB::bind_method("_scalar_op_changed",&ShaderGraphView::_scalar_op_changed);
+ ObjectTypeDB::bind_method("_vec_op_changed",&ShaderGraphView::_vec_op_changed);
+ ObjectTypeDB::bind_method("_vec_scalar_op_changed",&ShaderGraphView::_vec_scalar_op_changed);
+ ObjectTypeDB::bind_method("_rgb_op_changed",&ShaderGraphView::_rgb_op_changed);
+ ObjectTypeDB::bind_method("_xform_inv_rev_changed",&ShaderGraphView::_xform_inv_rev_changed);
+ ObjectTypeDB::bind_method("_scalar_func_changed",&ShaderGraphView::_scalar_func_changed);
+ ObjectTypeDB::bind_method("_vec_func_changed",&ShaderGraphView::_vec_func_changed);
+ ObjectTypeDB::bind_method("_scalar_input_changed",&ShaderGraphView::_scalar_input_changed);
+ ObjectTypeDB::bind_method("_vec_input_changed",&ShaderGraphView::_vec_input_changed);
+ ObjectTypeDB::bind_method("_xform_input_changed",&ShaderGraphView::_xform_input_changed);
+ ObjectTypeDB::bind_method("_rgb_input_changed",&ShaderGraphView::_rgb_input_changed);
+ ObjectTypeDB::bind_method("_tex_input_change",&ShaderGraphView::_tex_input_change);
+ ObjectTypeDB::bind_method("_cube_input_change",&ShaderGraphView::_cube_input_change);
+ ObjectTypeDB::bind_method("_input_name_changed",&ShaderGraphView::_input_name_changed);
+ ObjectTypeDB::bind_method("_tex_edited",&ShaderGraphView::_tex_edited);
+ ObjectTypeDB::bind_method("_variant_edited",&ShaderGraphView::_variant_edited);
+ ObjectTypeDB::bind_method("_cube_edited",&ShaderGraphView::_cube_edited);
+ ObjectTypeDB::bind_method("_comment_edited",&ShaderGraphView::_comment_edited);
+ ObjectTypeDB::bind_method("_color_ramp_changed",&ShaderGraphView::_color_ramp_changed);
+ ObjectTypeDB::bind_method("_curve_changed",&ShaderGraphView::_curve_changed);
+
+ ObjectTypeDB::bind_method("_sg_updated",&ShaderGraphView::_sg_updated);
+}
- /* add popup */
+ShaderGraphView::ShaderGraphView(ShaderGraph::ShaderType p_type) {
+
+ type=p_type;
+ graph_edit = memnew( GraphEdit );
+ block_update=false;
+ ped_popup = memnew( CustomPropertyEditor );
+ graph_edit->add_child(ped_popup);
+ status = memnew( Label );
+ graph_edit->get_top_layer()->add_child(status);
+ graph_edit->connect("_begin_node_move", this, "_begin_node_move");
+ graph_edit->connect("_end_node_move", this, "_end_node_move");
+ status->set_pos(Vector2(5,5));
+ status->add_color_override("font_color_shadow",Color(0,0,0));
+ status->add_color_override("font_color",Color(1,0.4,0.3));
+ status->add_constant_override("shadow_as_outline",1);
+ status->add_constant_override("shadow_offset_x",2);
+ status->add_constant_override("shadow_offset_y",2);
+ status->set_text("");
+}
+
+
+//////////////edit//////////////
+void ShaderGraphEditor::edit(Ref<ShaderGraph> p_shader) {
+
+ for(int i=0;i<ShaderGraph::SHADER_TYPE_MAX;i++) {
+ graph_edits[i]->set_graph(p_shader);
+ }
+}
- add_popup = memnew( Popup );
- add_child(add_popup);
- add_popup->set_as_toplevel(true);
- Panel *add_panel = memnew( Panel );
- add_popup->add_child(add_panel);
- add_panel->set_area_as_parent_rect();
+void ShaderGraphEditor::_add_node(int p_type) {
- Label *add_label = memnew (Label );
- add_label->set_pos(Point2(5,5));
- add_label->set_text("Available Nodes:");
- add_panel->add_child(add_label);
+ ShaderGraph::ShaderType shader_type=ShaderGraph::ShaderType(tabs->get_current_tab());
+ graph_edits[shader_type]->add_node(p_type, next_location);
+}
- add_types = memnew( Tree );
- add_types->set_anchor( MARGIN_RIGHT, ANCHOR_END );
- add_types->set_anchor( MARGIN_BOTTOM, ANCHOR_END );
- add_types->set_begin( Point2( 20,25 ) );
- add_types->set_end( Point2( 10, 30 ) );
- add_types->set_hide_root(true);
- add_types->set_columns(4);
- add_types->set_select_mode(Tree::SELECT_ROW);
+void ShaderGraphEditor::_popup_requested(const Vector2 &p_position)
+{
+ next_location = get_local_mouse_pos();
+ popup->set_global_pos(p_position);
+ popup->set_size( Size2( 200, 0) );
+ popup->popup();
+ popup->call_deferred("grab_click_focus");
+ popup->set_invalidate_click_until_motion();
+}
+void ShaderGraphEditor::_notification(int p_what) {
+ if (p_what==NOTIFICATION_ENTER_TREE) {
- TreeItem *add_types_root = add_types->create_item(NULL);
- TreeItem *info_item = add_types->create_item(add_types_root);
+ for(int i=0;i<ShaderGraph::NODE_TYPE_MAX;i++) {
- for(int i=0;i<VisualServer::NODE_TYPE_MAX;i++) {
+ if (i==ShaderGraph::NODE_OUTPUT)
+ continue;
+ if (!_2d && i==ShaderGraph::NODE_DEFAULT_TEXTURE)
+ continue;
- TreeItem *item = add_types->create_item(add_types_root);
- PropertyInfo prop = VisualServer::shader_node_get_type_info((VisualServer::ShaderNodeType)i);
- item->set_text(0,prop.name);
- item->set_text(1,itos(VisualServer::shader_get_input_count((VisualServer::ShaderNodeType)i)));
- item->set_text(2,itos(VisualServer::shader_get_output_count((VisualServer::ShaderNodeType)i)));
- String hint = (prop.type==Variant::_RID)?prop.hint_string:Variant::get_type_name(prop.type);
- item->set_text(3,hint);
- item->set_metadata(0,i);
- }
- info_item->set_text(0,"::NODE::");
- info_item->set_custom_color(0,Color(0.6,0.1,0.1));
- info_item->set_text(1,"::INPUTS::");
- info_item->set_custom_color(1,Color(0.6,0.1,0.1));
- info_item->set_text(2,"::OUTPUTS::");
- info_item->set_custom_color(2,Color(0.6,0.1,0.1));
- info_item->set_text(3,"::PARAM::");
- info_item->set_custom_color(3,Color(0.6,0.1,0.1));
- info_item->set_selectable(0,false);
- info_item->set_selectable(1,false);
- info_item->set_selectable(2,false);
- info_item->set_selectable(3,false);
+ String nn = node_names[i];
+ String ic = nn.get_slice(":",0);
+ String v = nn.get_slice(":",1);
+ bool addsep=false;
+ if (nn.ends_with(":")) {
+ addsep=true;
+ }
+ popup->add_icon_item(get_icon(ic,"EditorIcons"),v,i);
+ if (addsep)
+ popup->add_separator();
+ }
+ popup->connect("item_pressed",this,"_add_node");
- add_panel->add_child(add_types);
- add_confirm = memnew( Button );
- add_confirm->set_anchor( MARGIN_LEFT, ANCHOR_END );
- add_confirm->set_anchor( MARGIN_TOP, ANCHOR_END );
- add_confirm->set_anchor( MARGIN_RIGHT, ANCHOR_END );
- add_confirm->set_anchor( MARGIN_BOTTOM, ANCHOR_END );
- add_confirm->set_begin( Point2( 75, 29 ) );
- add_confirm->set_end( Point2( 10, 15 ) );
- add_confirm->set_text("Add");
- add_panel->add_child(add_confirm);
- add_confirm->connect("pressed", this,"_node_add_callback");
+ }
+}
- last_id=1;
- last_x=20;
- last_y=20;
+void ShaderGraphEditor::_bind_methods() {
- property_editor = memnew( CustomPropertyEditor );
- add_child(property_editor);
- property_editor->connect("variant_changed", this,"_node_param_changed");
+ ObjectTypeDB::bind_method("_add_node",&ShaderGraphEditor::_add_node);
+ ObjectTypeDB::bind_method("_popup_requested",&ShaderGraphEditor::_popup_requested);
+}
- h_scroll = memnew( HScrollBar );
- v_scroll = memnew( VScrollBar );
- add_child(h_scroll);
- add_child(v_scroll);
+const char* ShaderGraphEditor::node_names[ShaderGraph::NODE_TYPE_MAX]={
+ "GraphInput:Input", // all inputs (shader type dependent)
+ "GraphScalar:Scalar Constant", //scalar constant
+ "GraphVector:Vector Constant", //vec3 constant
+ "GraphRgb:RGB Constant", //rgb constant (shows a color picker instead)
+ "GraphXform:XForm Constant", // 4x4 matrix constant
+ "GraphTime:Time:", // time in seconds
+ "GraphTexscreen:Screen Sample", // screen texture sampler (takes uv) (only usable in fragment shader)
+ "GraphScalarOp:Scalar Operator", // scalar vs scalar op (mul", add", div", etc)
+ "GraphVecOp:Vector Operator", // vec3 vs vec3 op (mul",ad",div",crossprod",etc)
+ "GraphVecScalarOp:Scalar+Vector Operator", // vec3 vs scalar op (mul", add", div", etc)
+ "GraphRgbOp:RGB Operator:", // vec3 vs vec3 rgb op (with scalar amount)", like brighten", darken", burn", dodge", multiply", etc.
+ "GraphXformMult:XForm Multiply", // mat4 x mat4
+ "GraphXformVecMult:XForm+Vector Multiply", // mat4 x vec3 mult (with no-translation option)
+ "GraphXformVecImult:Form+Vector InvMultiply:", // mat4 x vec3 inverse mult (with no-translation option)
+ "GraphXformScalarFunc:Scalar Function", // scalar function (sin", cos", etc)
+ "GraphXformVecFunc:Vector Function", // vector function (normalize", negate", reciprocal", rgb2hsv", hsv2rgb", etc", etc)
+ "GraphVecLength:Vector Length", // vec3 length
+ "GraphVecDp:Dot Product:", // vec3 . vec3 (dot product -> scalar output)
+ "GraphVecToScalars:Vector -> Scalars", // 1 vec3 input", 3 scalar outputs
+ "GraphScalarsToVec:Scalars -> Vector", // 3 scalar input", 1 vec3 output
+ "GraphXformToVecs:XForm -> Vectors", // 3 vec input", 1 xform output
+ "GraphVecsToXform:Vectors -> XForm:", // 3 vec input", 1 xform output
+ "GraphScalarInterp:Scalar Interpolate", // scalar interpolation (with optional curve)
+ "GraphVecInterp:Vector Interpolate:", // vec3 interpolation (with optional curve)
+ "GraphColorRamp:Color Ramp", // vec3 interpolation (with optional curve)
+ "GraphCurveMap:Curve Remap:", // vec3 interpolation (with optional curve)
+ "GraphScalarUniform:Scalar Uniform", // scalar uniform (assignable in material)
+ "GraphVectorUniform:Vector Uniform", // vec3 uniform (assignable in material)
+ "GraphRgbUniform:RGB Uniform", // color uniform (assignable in material)
+ "GraphXformUniform:XForm Uniform", // mat4 uniform (assignable in material)
+ "GraphTextureUniform:Texture Uniform", // texture input (assignable in material)
+ "GraphCubeUniform:CubeMap Uniform:", // cubemap input (assignable in material)
+ "GraphDefaultTexture:CanvasItem Texture:", // cubemap input (assignable in material)
+ "Output", // output (shader type dependent)
+ "GraphComment:Comment", // comment
- h_scroll->connect("value_changed", this,"_scroll_moved");
- v_scroll->connect("value_changed", this,"_scroll_moved");
- node_popup= memnew(PopupMenu );
- add_child(node_popup);
- node_popup->set_as_toplevel(true);
+};
+ShaderGraphEditor::ShaderGraphEditor(bool p_2d) {
+ _2d=p_2d;
+
+ popup = memnew( PopupMenu );
+ add_child(popup);
+
+
+ tabs = memnew(TabContainer);
+ tabs->set_v_size_flags(SIZE_EXPAND_FILL);
+ add_child(tabs);
+ const char* sname[ShaderGraph::SHADER_TYPE_MAX]={
+ "Vertex",
+ "Fragment",
+ "Light"
+ };
+ for(int i=0;i<ShaderGraph::SHADER_TYPE_MAX;i++) {
+
+ graph_edits[i]= memnew( ShaderGraphView(ShaderGraph::ShaderType(i)) );
+ add_child(graph_edits[i]);
+ graph_edits[i]->get_graph_edit()->set_name(sname[i]);
+ tabs->add_child(graph_edits[i]->get_graph_edit());
+ graph_edits[i]->get_graph_edit()->connect("connection_request",graph_edits[i],"_connection_request");
+ graph_edits[i]->get_graph_edit()->connect("disconnection_request",graph_edits[i],"_disconnection_request");
+ graph_edits[i]->get_graph_edit()->connect("duplicate_nodes_request", graph_edits[i], "_duplicate_nodes_request");
+ graph_edits[i]->get_graph_edit()->connect("popup_request",this,"_popup_requested");
+ graph_edits[i]->get_graph_edit()->connect("delete_nodes_request",graph_edits[i],"_delete_nodes_request");
+ graph_edits[i]->get_graph_edit()->set_right_disconnects(true);
+ }
- node_popup->connect("item_pressed", this,"_node_menu_item");
+ tabs->set_current_tab(1);
+ set_custom_minimum_size(Size2(100,300));
}
-void ShaderEditorPlugin::edit(Object *p_object) {
+void ShaderGraphEditorPlugin::edit(Object *p_object) {
- shader_editor->edit(p_object->cast_to<Shader>());
+ shader_editor->edit(p_object->cast_to<ShaderGraph>());
}
-bool ShaderEditorPlugin::handles(Object *p_object) const {
+bool ShaderGraphEditorPlugin::handles(Object *p_object) const {
- return p_object->is_type("Shader");
+ ShaderGraph *shader=p_object->cast_to<ShaderGraph>();
+ if (!shader)
+ return false;
+ if (_2d)
+ return shader->get_mode()==Shader::MODE_CANVAS_ITEM;
+ else
+ return shader->get_mode()==Shader::MODE_MATERIAL;
}
-void ShaderEditorPlugin::make_visible(bool p_visible) {
+void ShaderGraphEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
shader_editor->show();
- shader_editor->set_process(true);
} else {
shader_editor->hide();
- shader_editor->set_process(false);
}
}
-ShaderEditorPlugin::ShaderEditorPlugin(EditorNode *p_node) {
+ShaderGraphEditorPlugin::ShaderGraphEditorPlugin(EditorNode *p_node, bool p_2d) {
+ _2d=p_2d;
editor=p_node;
- shader_editor = memnew( ShaderEditor );
- editor->get_viewport()->add_child(shader_editor);
- shader_editor->set_area_as_parent_rect();
+ shader_editor = memnew( ShaderGraphEditor(p_2d) );
shader_editor->hide();
+ if (p_2d)
+ CanvasItemEditor::get_singleton()->get_bottom_split()->add_child(shader_editor);
+ else
+ SpatialEditor::get_singleton()->get_shader_split()->add_child(shader_editor);
+ // editor->get_viewport()->add_child(shader_editor);
+ // shader_editor->set_area_as_parent_rect();
+ // shader_editor->hide();
}
-ShaderEditorPlugin::~ShaderEditorPlugin()
+ShaderGraphEditorPlugin::~ShaderGraphEditorPlugin()
{
}
-#endif
+
diff --git a/tools/editor/plugins/shader_graph_editor_plugin.h b/tools/editor/plugins/shader_graph_editor_plugin.h
index 5b0767dc82..39e9b29d45 100644
--- a/tools/editor/plugins/shader_graph_editor_plugin.h
+++ b/tools/editor/plugins/shader_graph_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,122 +29,211 @@
#ifndef SHADER_GRAPH_EDITOR_PLUGIN_H
#define SHADER_GRAPH_EDITOR_PLUGIN_H
-#if 0
+
#include "tools/editor/editor_plugin.h"
#include "tools/editor/editor_node.h"
#include "scene/resources/shader.h"
#include "servers/visual/shader_graph.h"
#include "scene/gui/tree.h"
#include "scene/gui/button.h"
+#include "scene/gui/graph_edit.h"
#include "scene/gui/popup.h"
#include "tools/editor/property_editor.h"
+#include "scene/resources/shader_graph.h"
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
-class ShaderEditor : public Control {
- OBJ_TYPE(ShaderEditor, Control );
+class GraphColorRampEdit : public Control {
+
+ OBJ_TYPE(GraphColorRampEdit,Control);
- enum MenuAction {
- GRAPH_ADD_NODE,
- GRAPH_CLEAR,
- NODE_DISCONNECT,
- NODE_ERASE,
+ struct Point {
+ float offset;
+ Color color;
+ bool operator<(const Point& p_ponit) const {
+ return offset<p_ponit.offset;
+ }
};
- enum ClickType {
- CLICK_NONE,
- CLICK_NODE,
- CLICK_INPUT_SLOT,
- CLICK_OUTPUT_SLOT,
- CLICK_PARAMETER
+ PopupPanel *popup;
+ ColorPicker *picker;
+
+
+ bool grabbing;
+ int grabbed;
+ float grabbed_at;
+ Vector<Point> points;
+
+ void _color_changed(const Color& p_color);
+
+protected:
+ void _input_event(const InputEvent& p_event);
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_ramp(const Vector<float>& p_offsets,const Vector<Color>& p_colors);
+ Vector<float> get_offsets() const;
+ Vector<Color> get_colors() const;
+ virtual Size2 get_minimum_size() const;
+ GraphColorRampEdit();
+};
+
+
+class GraphCurveMapEdit : public Control {
+
+ OBJ_TYPE(GraphCurveMapEdit,Control);
+
+
+ struct Point {
+
+ float offset;
+ float height;
+ bool operator<(const Point& p_ponit) const {
+ return offset<p_ponit.offset;
+ }
};
- PopupMenu *node_popup;
- Popup *add_popup;
- PopupMenu *vertex_popup;
- PopupMenu *fragment_popup;
- PopupMenu *post_popup;
- Tree *add_types;
- Button *add_confirm;
- HScrollBar *h_scroll;
- VScrollBar *v_scroll;
-
- Ref<Shader> shader;
- List<int> order;
- Set<int> active_nodes;
- ShaderGraph shader_graph;
- int last_x,last_y;
- uint32_t last_id;
-
- CustomPropertyEditor *property_editor;
-
- Point2 offset;
- ClickType click_type;
- Point2 click_pos;
- int click_node;
- int click_slot;
- Point2 click_motion;
- ClickType rclick_type;
- int rclick_node;
- int rclick_slot;
-
- Size2 _get_maximum_size();
- Size2 get_node_size(int p_node) const;
- void _draw_node(int p_node);
-
- void _add_node_from_text(const String& p_text);
- void _update_scrollbars();
- void _scroll_moved();
- void _node_param_changed();
- void _node_add_callback();
- void _node_add(VisualServer::ShaderNodeType p_type);
- void _node_edit_property(int p_node);
- void _node_menu_item(int p_item);
- void _vertex_item(int p_item);
- void _fragment_item(int p_item);
- void _post_item(int p_item);
-
- ClickType _locate_click(const Point2& p_click,int *p_node_id,int *p_slot_index) const;
- Point2 _get_slot_pos(int p_node_id,bool p_input,int p_slot);
-
- Error validate_graph();
-
- void _read_shader_graph();
- void _write_shader_graph();
-
- virtual bool has_point(const Point2& p_point) const;
+
+ bool grabbing;
+ int grabbed;
+ Vector<Point> points;
+
+ void _plot_curve(const Vector2& p_a,const Vector2& p_b,const Vector2& p_c,const Vector2& p_d);
protected:
+ void _input_event(const InputEvent& p_event);
void _notification(int p_what);
- void _input_event(InputEvent p_event);
static void _bind_methods();
public:
- void edit(Ref<Shader> p_shader);
- ShaderEditor();
+ void set_points(const Vector<Vector2>& p_points);
+ Vector<Vector2> get_points() const;
+ virtual Size2 get_minimum_size() const;
+ GraphCurveMapEdit();
};
-class ShaderEditorPlugin : public EditorPlugin {
+class ShaderGraphView : public Node {
+
+ OBJ_TYPE(ShaderGraphView,Node);
+
+
+
+ CustomPropertyEditor *ped_popup;
+ bool block_update;
+
+ Label *status;
+ GraphEdit *graph_edit;
+ Ref<ShaderGraph> graph;
+ int edited_id;
+ int edited_def;
+
+ ShaderGraph::ShaderType type;
+
+ void _update_graph();
+ void _create_node(int p_id);
+
+
+ ToolButton *make_label(String text, Variant::Type v_type = Variant::NIL);
+ ToolButton *make_editor(String text, GraphNode* gn, int p_id, int param, Variant::Type type, String p_hint="");
+
+ void _connection_request(const String& p_from, int p_from_slot,const String& p_to,int p_to_slot);
+ void _disconnection_request(const String& p_from, int p_from_slot,const String& p_to,int p_to_slot);
+
+ void _node_removed(int p_id);
+ void _begin_node_move();
+ void _node_moved(const Vector2& p_from, const Vector2& p_to,int p_id);
+ void _end_node_move();
+ void _move_node(int p_id,const Vector2& p_to);
+ void _duplicate_nodes_request();
+ void _duplicate_nodes(const Array &p_nodes);
+ void _delete_nodes_request();
+
+
+ void _default_changed(int p_id, Node* p_button, int p_param, int v_type, String p_hint);
+
+ void _scalar_const_changed(double p_value,int p_id);
+ void _vec_const_changed(double p_value, int p_id, Array p_arr);
+ void _rgb_const_changed(const Color& p_color, int p_id);
+ void _xform_const_changed(int p_id,Node* p_button);
+ void _scalar_op_changed(int p_op, int p_id);
+ void _vec_op_changed(int p_op, int p_id);
+ void _vec_scalar_op_changed(int p_op, int p_id);
+ void _rgb_op_changed(int p_op, int p_id);
+ void _xform_inv_rev_changed(bool p_enabled, int p_id);
+ void _scalar_func_changed(int p_func, int p_id);
+ void _vec_func_changed(int p_func, int p_id);
+ void _scalar_input_changed(double p_value,int p_id);
+ void _vec_input_changed(double p_value, int p_id, Array p_arr);
+ void _xform_input_changed(int p_id,Node* p_button);
+ void _rgb_input_changed(const Color& p_color, int p_id);
+ void _tex_input_change(int p_id,Node* p_button);
+ void _cube_input_change(int p_id);
+ void _input_name_changed(const String& p_name,int p_id,Node* p_line_edit);
+ void _tex_edited(int p_id,Node* p_button);
+ void _cube_edited(int p_id,Node* p_button);
+ void _variant_edited();
+ void _comment_edited(int p_id,Node* p_button);
+ void _color_ramp_changed(int p_id,Node* p_ramp);
+ void _curve_changed(int p_id,Node* p_curve);
+ void _sg_updated();
+ Map<int,GraphNode*> node_map;
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void add_node(int p_type, const Vector2 &location);
+ GraphEdit *get_graph_edit() { return graph_edit; }
+ void set_graph(Ref<ShaderGraph> p_graph);
- OBJ_TYPE( ShaderEditorPlugin, EditorPlugin );
+ ShaderGraphView(ShaderGraph::ShaderType p_type=ShaderGraph::SHADER_TYPE_FRAGMENT);
+};
+
+class ShaderGraphEditor : public VBoxContainer {
+
+ OBJ_TYPE(ShaderGraphEditor,VBoxContainer);
- ShaderEditor *shader_editor;
+ PopupMenu *popup;
+ TabContainer *tabs;
+ ShaderGraphView *graph_edits[ShaderGraph::SHADER_TYPE_MAX];
+ static const char* node_names[ShaderGraph::NODE_TYPE_MAX];
+ Vector2 next_location;
+
+ bool _2d;
+ void _add_node(int p_type);
+ void _popup_requested(const Vector2 &p_position);
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void edit(Ref<ShaderGraph> p_shader);
+ ShaderGraphEditor(bool p_2d);
+};
+
+class ShaderGraphEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( ShaderGraphEditorPlugin, EditorPlugin );
+
+ bool _2d;
+ ShaderGraphEditor *shader_editor;
EditorNode *editor;
public:
- virtual String get_name() const { return "Shader"; }
+ virtual String get_name() const { return "ShaderGraph"; }
bool has_main_screen() const { return false; }
virtual void edit(Object *p_node);
virtual bool handles(Object *p_node) const;
virtual void make_visible(bool p_visible);
- ShaderEditorPlugin(EditorNode *p_node);
- ~ShaderEditorPlugin();
+ ShaderGraphEditorPlugin(EditorNode *p_node,bool p_2d);
+ ~ShaderGraphEditorPlugin();
};
#endif
-#endif // SHADER_GRAPH_EDITOR_PLUGIN_H
+
diff --git a/tools/editor/plugins/spatial_editor_plugin.cpp b/tools/editor/plugins/spatial_editor_plugin.cpp
index a1f1ccf5e3..7816efe89f 100644
--- a/tools/editor/plugins/spatial_editor_plugin.cpp
+++ b/tools/editor/plugins/spatial_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -232,15 +232,6 @@ void SpatialEditorViewport::_select(Spatial *p_node, bool p_append,bool p_single
}
-
-struct _RayResult {
-
- Spatial* item;
- float depth;
- int handle;
- _FORCE_INLINE_ bool operator<(const _RayResult& p_rr) const { return depth<p_rr.depth; }
-};
-
ObjectID SpatialEditorViewport::_select_ray(const Point2& p_pos, bool p_append,bool &r_includes_current,int *r_gizmo_handle,bool p_alt_select) {
if (r_gizmo_handle)
@@ -379,6 +370,70 @@ ObjectID SpatialEditorViewport::_select_ray(const Point2& p_pos, bool p_append,b
}
+void SpatialEditorViewport::_find_items_at_pos(const Point2& p_pos,bool &r_includes_current,Vector<_RayResult> &results,bool p_alt_select) {
+
+ Vector3 ray=_get_ray(p_pos);
+ Vector3 pos=_get_ray_pos(p_pos);
+
+ Vector<RID> instances=VisualServer::get_singleton()->instances_cull_ray(pos,ray,get_tree()->get_root()->get_world()->get_scenario() );
+ Set<Ref<SpatialEditorGizmo> > found_gizmos;
+
+ r_includes_current=false;
+
+ for (int i=0;i<instances.size();i++) {
+
+ uint32_t id=VisualServer::get_singleton()->instance_get_object_instance_ID(instances[i]);
+ Object *obj=ObjectDB::get_instance(id);
+ if (!obj)
+ continue;
+
+ Spatial *spat=obj->cast_to<Spatial>();
+
+ if (!spat)
+ continue;
+
+ Ref<SpatialEditorGizmo> seg = spat->get_gizmo();
+
+ if (!seg.is_valid())
+ continue;
+
+ if (found_gizmos.has(seg))
+ continue;
+
+ found_gizmos.insert(seg);
+ Vector3 point;
+ Vector3 normal;
+
+ int handle=-1;
+ bool inters = seg->intersect_ray(camera,p_pos,point,normal,NULL,p_alt_select);
+
+ if (!inters)
+ continue;
+
+ float dist = pos.distance_to(point);
+
+ if (dist<0)
+ continue;
+
+
+
+ if (editor_selection->is_selected(spat))
+ r_includes_current=true;
+
+ _RayResult res;
+ res.item=spat;
+ res.depth=dist;
+ res.handle=handle;
+ results.push_back(res);
+ }
+
+
+ if (results.empty())
+ return;
+
+ results.sort();
+}
+
Vector3 SpatialEditorViewport::_get_screen_to_space(const Vector3& p_pos) {
@@ -477,6 +532,16 @@ void SpatialEditorViewport::_select_region() {
}
+void SpatialEditorViewport::_update_name() {
+
+ String ortho = orthogonal?"Orthogonal":"Perspective";
+
+ if (name!="")
+ view_menu->set_text("[ "+name+" "+ortho+" ]");
+ else
+ view_menu->set_text("[ "+ortho+" ]");
+}
+
void SpatialEditorViewport::_compute_edit(const Point2& p_point) {
@@ -535,6 +600,14 @@ SpatialEditorViewport::NavigationScheme SpatialEditorViewport::_get_navigation_s
return NAVIGATION_GODOT;
}
+SpatialEditorViewport::NavigationZoomStyle SpatialEditorViewport::_get_navigation_zoom_style(const String& p_property) {
+ switch(EditorSettings::get_singleton()->get(p_property).operator int()) {
+ case 0: return NAVIGATION_ZOOM_VERTICAL;
+ case 1: return NAVIGATION_ZOOM_HORIZONTAL;
+ }
+ return NAVIGATION_ZOOM_VERTICAL;
+}
+
bool SpatialEditorViewport::_gizmo_select(const Vector2& p_screenpos,bool p_hilite_only) {
if (!spatial_editor->is_gizmo_visible())
@@ -659,7 +732,8 @@ bool SpatialEditorViewport::_gizmo_select(const Vector2& p_screenpos,bool p_hili
void SpatialEditorViewport::_smouseenter() {
- surface->grab_focus();
+ if (!surface->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field()))
+ surface->grab_focus();
}
void SpatialEditorViewport::_sinput(const InputEvent &p_event) {
@@ -705,6 +779,7 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) {
} break;
case BUTTON_RIGHT: {
+ NavigationScheme nav_scheme = _get_navigation_schema("3d_editor/navigation_scheme");
if (b.pressed && _edit.gizmo.is_valid()) {
//restore
@@ -787,6 +862,57 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) {
//VisualServer::get_singleton()->instance_set_transform(cursor_instance,Transform(Matrix3(),cursor.cursor_pos));
}
}
+
+ if (b.mod.alt) {
+
+ if (nav_scheme == NAVIGATION_MAYA)
+ break;
+
+ _find_items_at_pos(Vector2( b.x, b.y ),clicked_includes_current,selection_results,b.mod.shift);
+
+ clicked_wants_append=b.mod.shift;
+
+ if (selection_results.size() == 1) {
+
+ clicked=selection_results[0].item->get_instance_ID();
+ selection_results.clear();
+
+ if (clicked) {
+ _select_clicked(clicked_wants_append,true);
+ clicked=0;
+ }
+
+ } else if (!selection_results.empty()) {
+
+ NodePath root_path = get_tree()->get_edited_scene_root()->get_path();
+ StringName root_name = root_path.get_name(root_path.get_name_count()-1);
+
+ for (int i = 0; i < selection_results.size(); i++) {
+
+ Spatial *spat=selection_results[i].item;
+
+ Ref<Texture> icon;
+ if (spat->has_meta("_editor_icon"))
+ icon=spat->get_meta("_editor_icon");
+ else
+ icon=get_icon( has_icon(spat->get_type(),"EditorIcons")?spat->get_type():String("Object"),"EditorIcons");
+
+ String node_path="/"+root_name+"/"+root_path.rel_path_to(spat->get_path());
+
+ selection_menu->add_item(spat->get_name());
+ selection_menu->set_item_icon(i, icon );
+ selection_menu->set_item_metadata(i, node_path);
+ selection_menu->set_item_tooltip(i,String(spat->get_name())+
+ "\nType: "+spat->get_type()+"\nPath: "+node_path);
+ }
+
+ selection_menu->set_global_pos(Vector2( b.global_x, b.global_y ));
+ selection_menu->popup();
+ selection_menu->call_deferred("grab_click_focus");
+
+ break;
+ }
+ }
}
if (_edit.mode!=TRANSFORM_NONE && b.pressed) {
@@ -824,6 +950,8 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) {
_edit.plane=TRANSFORM_X_AXIS;
set_message("View Plane Transform.",2);
+ name="";
+ _update_name();
} break;
case TRANSFORM_X_AXIS: {
@@ -1429,10 +1557,19 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) {
if (nav_scheme==NAVIGATION_MAYA && m.mod.shift)
zoom_speed *= zoom_speed_modifier;
- if ( m.relative_y > 0)
- cursor.distance*=1+m.relative_y*zoom_speed;
- else if (m.relative_y < 0)
- cursor.distance/=1-m.relative_y*zoom_speed;
+ NavigationZoomStyle zoom_style = _get_navigation_zoom_style("3d_editor/zoom_style");
+ if (zoom_style == NAVIGATION_ZOOM_HORIZONTAL) {
+ if ( m.relative_x > 0)
+ cursor.distance*=1-m.relative_x*zoom_speed;
+ else if (m.relative_x < 0)
+ cursor.distance/=1+m.relative_x*zoom_speed;
+ }
+ else {
+ if ( m.relative_y > 0)
+ cursor.distance*=1+m.relative_y*zoom_speed;
+ else if (m.relative_y < 0)
+ cursor.distance/=1-m.relative_y*zoom_speed;
+ }
} break;
@@ -1443,6 +1580,8 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) {
cursor.x_rot=Math_PI/2.0;
if (cursor.x_rot<-Math_PI/2.0)
cursor.x_rot=-Math_PI/2.0;
+ name="";
+ _update_name();
} break;
default: {}
@@ -1467,9 +1606,14 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) {
if (k.mod.shift) {
cursor.x_rot=-Math_PI/2.0;
set_message("Bottom View.",2);
+ name="Bottom";
+ _update_name();
+
} else {
cursor.x_rot=Math_PI/2.0;
set_message("Top View.",2);
+ name="Top";
+ _update_name();
}
} break;
case KEY_KP_1: {
@@ -1478,10 +1622,14 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) {
if (k.mod.shift) {
cursor.y_rot=Math_PI;
set_message("Rear View.",2);
+ name="Rear";
+ _update_name();
} else {
cursor.y_rot=0;
set_message("Front View.",2);
+ name="Front";
+ _update_name();
}
} break;
@@ -1491,9 +1639,13 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) {
if (k.mod.shift) {
cursor.y_rot=Math_PI/2.0;
set_message("Left View.",2);
+ name="Left";
+ _update_name();
} else {
cursor.y_rot=-Math_PI/2.0;
set_message("Right View.",2);
+ name="Right";
+ _update_name();
}
} break;
@@ -1501,6 +1653,7 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) {
orthogonal = !orthogonal;
_menu_option(orthogonal?VIEW_PERSPECTIVE:VIEW_ORTHOGONAL);
+ _update_name();
} break;
@@ -1720,6 +1873,12 @@ void SpatialEditorViewport::_notification(int p_what) {
_init_gizmo_instance(index);
}
+ if (p_what==NOTIFICATION_EXIT_TREE) {
+
+
+ _finish_gizmo_instances();
+
+ }
if (p_what==NOTIFICATION_MOUSE_ENTER) {
@@ -1814,35 +1973,47 @@ void SpatialEditorViewport::_menu_option(int p_option) {
cursor.x_rot=Math_PI/2.0;
cursor.y_rot=0;
+ name="Top";
+ _update_name();
} break;
case VIEW_BOTTOM: {
cursor.x_rot=-Math_PI/2.0;
cursor.y_rot=0;
+ name="Bottom";
+ _update_name();
} break;
case VIEW_LEFT: {
cursor.y_rot=Math_PI/2.0;
cursor.x_rot=0;
+ name="Left";
+ _update_name();
} break;
case VIEW_RIGHT: {
cursor.y_rot=-Math_PI/2.0;
cursor.x_rot=0;
+ name="Right";
+ _update_name();
} break;
case VIEW_FRONT: {
cursor.y_rot=0;
cursor.x_rot=0;
+ name="Front";
+ _update_name();
} break;
case VIEW_REAR: {
cursor.y_rot=Math_PI;
cursor.x_rot=0;
+ name="Rear";
+ _update_name();
} break;
case VIEW_CENTER_TO_SELECTION: {
@@ -1893,11 +2064,11 @@ void SpatialEditorViewport::_menu_option(int p_option) {
if (!se)
continue;
- Vector3 original_scale = sp->get_scale();
- sp->set_global_transform(camera_transform);
- sp->set_scale(original_scale);
- undo_redo->add_do_method(sp,"set_global_transform",sp->get_global_transform());
- undo_redo->add_undo_method(sp,"set_global_transform",se->original);
+ Transform xform = camera_transform;
+ xform.scale_basis(sp->get_scale());
+
+ undo_redo->add_do_method(sp,"set_global_transform",xform);
+ undo_redo->add_undo_method(sp,"set_global_transform",sp->get_global_transform());
}
undo_redo->commit_action();
} break;
@@ -1924,6 +2095,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), false );
orthogonal=false;
call_deferred("update_transform_gizmo_view");
+ _update_name();
} break;
case VIEW_ORTHOGONAL: {
@@ -1932,6 +2104,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), true );
orthogonal=true;
call_deferred("update_transform_gizmo_view");
+ _update_name();
} break;
case VIEW_AUDIO_LISTENER: {
@@ -1993,6 +2166,16 @@ void SpatialEditorViewport::_init_gizmo_instance(int p_idx) {
}
+
+void SpatialEditorViewport::_finish_gizmo_instances() {
+
+
+ for(int i=0;i<3;i++) {
+ VS::get_singleton()->free(move_gizmo_instance[i]);
+ VS::get_singleton()->free(rotate_gizmo_instance[i]);
+ }
+
+}
void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) {
@@ -2020,6 +2203,26 @@ void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) {
}
}
+void SpatialEditorViewport::_selection_result_pressed(int p_result) {
+
+ if (selection_results.size() <= p_result)
+ return;
+
+ clicked=selection_results[p_result].item->get_instance_ID();
+
+ if (clicked) {
+ _select_clicked(clicked_wants_append,true);
+ clicked=0;
+ }
+}
+
+void SpatialEditorViewport::_selection_menu_hide() {
+
+ selection_results.clear();
+ selection_menu->clear();
+ selection_menu->set_size(Vector2(0, 0));
+}
+
void SpatialEditorViewport::set_can_preview(Camera* p_preview) {
preview=p_preview;
@@ -2093,7 +2296,18 @@ void SpatialEditorViewport::set_state(const Dictionary& p_state) {
view_menu->get_popup()->set_item_checked( idx, listener );
}
-
+ if (p_state.has("previewing")) {
+ Node *pv = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["previewing"]);
+ if (pv && pv->cast_to<Camera>()) {
+ previewing=pv->cast_to<Camera>();
+ previewing->connect("exit_tree",this,"_preview_exited_scene");
+ VS::get_singleton()->viewport_attach_camera( viewport->get_viewport(), previewing->get_camera() ); //replace
+ view_menu->hide();
+ surface->update();
+ preview_camera->set_pressed(true);
+ preview_camera->show();
+ }
+ }
}
Dictionary SpatialEditorViewport::get_state() const {
@@ -2106,6 +2320,10 @@ Dictionary SpatialEditorViewport::get_state() const {
d["use_environment"]=camera->get_environment().is_valid();
d["use_orthogonal"]=camera->get_projection()==Camera::PROJECTION_ORTHOGONAL;
d["listener"]=viewport->is_audio_listener();
+ if (previewing) {
+ d["previewing"]=EditorNode::get_singleton()->get_edited_scene()->get_path_to(previewing);
+ }
+
return d;
}
@@ -2119,6 +2337,8 @@ void SpatialEditorViewport::_bind_methods(){
ObjectTypeDB::bind_method(_MD("_toggle_camera_preview"),&SpatialEditorViewport::_toggle_camera_preview);
ObjectTypeDB::bind_method(_MD("_preview_exited_scene"),&SpatialEditorViewport::_preview_exited_scene);
ObjectTypeDB::bind_method(_MD("update_transform_gizmo_view"),&SpatialEditorViewport::update_transform_gizmo_view);
+ ObjectTypeDB::bind_method(_MD("_selection_result_pressed"),&SpatialEditorViewport::_selection_result_pressed);
+ ObjectTypeDB::bind_method(_MD("_selection_menu_hide"),&SpatialEditorViewport::_selection_menu_hide);
ADD_SIGNAL( MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport")) );
}
@@ -2130,18 +2350,24 @@ void SpatialEditorViewport::reset() {
message_time=0;
message="";
last_message="";
+ name="Top";
cursor.x_rot=0;
cursor.y_rot=0;
cursor.distance=4;
cursor.region_select=false;
+ _update_name();
+}
+SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, EditorNode *p_editor, int p_index) {
+ _edit.mode=TRANSFORM_NONE;
+ _edit.plane=TRANSFORM_VIEW;
+ _edit.edited_gizmo=0;
+ _edit.snap=1;
+ _edit.gizmo_handle=0;
-}
-
-SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, EditorNode *p_editor, int p_index) {
index=p_index;
editor=p_editor;
@@ -2172,18 +2398,17 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
view_menu = memnew( MenuButton );
surface->add_child(view_menu);
view_menu->set_pos( Point2(4,4));
- view_menu->set_text("[view]");
view_menu->set_self_opacity(0.5);
- view_menu->get_popup()->add_item("Top",VIEW_TOP);
- view_menu->get_popup()->add_item("Bottom",VIEW_BOTTOM);
- view_menu->get_popup()->add_item("Left",VIEW_LEFT);
- view_menu->get_popup()->add_item("Right",VIEW_RIGHT);
- view_menu->get_popup()->add_item("Front",VIEW_FRONT);
- view_menu->get_popup()->add_item("Rear",VIEW_REAR);
+ view_menu->get_popup()->add_item("Top (Num7)",VIEW_TOP);
+ view_menu->get_popup()->add_item("Bottom (Shift+Num7)",VIEW_BOTTOM);
+ view_menu->get_popup()->add_item("Left (Num3)",VIEW_LEFT);
+ view_menu->get_popup()->add_item("Right (Shift+Num3)",VIEW_RIGHT);
+ view_menu->get_popup()->add_item("Front (Num1)",VIEW_FRONT);
+ view_menu->get_popup()->add_item("Rear (Shift+Num1)",VIEW_REAR);
view_menu->get_popup()->add_separator();
- view_menu->get_popup()->add_check_item("Perspective",VIEW_PERSPECTIVE);
- view_menu->get_popup()->add_check_item("Orthogonal",VIEW_ORTHOGONAL);
+ view_menu->get_popup()->add_check_item("Perspective (Num5)",VIEW_PERSPECTIVE);
+ view_menu->get_popup()->add_check_item("Orthogonal (Num5)",VIEW_ORTHOGONAL);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE),true);
view_menu->get_popup()->add_separator();
view_menu->get_popup()->add_check_item("Environment",VIEW_ENVIRONMENT);
@@ -2211,11 +2436,21 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
preview=NULL;
gizmo_scale=1.0;
+ selection_menu = memnew( PopupMenu );
+ add_child(selection_menu);
+ selection_menu->set_custom_minimum_size(Vector2(100, 0));
+ selection_menu->connect("item_pressed", this, "_selection_result_pressed");
+ selection_menu->connect("popup_hide", this, "_selection_menu_hide");
+
if (p_index==0) {
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER),true);
viewport->set_as_audio_listener(true);
}
+
+ name="Top";
+ _update_name();
+
EditorSettings::get_singleton()->connect("settings_changed",this,"update_transform_gizmo_view");
}
@@ -2625,7 +2860,7 @@ void SpatialEditor::_menu_item_pressed(int p_option) {
} break;
case MENU_TRANSFORM_CONFIGURE_SNAP: {
- snap_dialog->popup_centered(Size2(200,160));
+ snap_dialog->popup_centered(Size2(200,180));
} break;
case MENU_TRANSFORM_LOCAL_COORDS: {
@@ -2911,14 +3146,14 @@ void SpatialEditor::_init_indicators() {
VisualServer::get_singleton()->instance_set_transform(light_instance,light_transform);
- RID mat = VisualServer::get_singleton()->fixed_material_create();
- VisualServer::get_singleton()->fixed_material_set_flag(mat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true);
- VisualServer::get_singleton()->fixed_material_set_flag(mat, VisualServer::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true);
+ //RID mat = VisualServer::get_singleton()->fixed_material_create();
+ ///VisualServer::get_singleton()->fixed_material_set_flag(mat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true);
+ //VisualServer::get_singleton()->fixed_material_set_flag(mat, VisualServer::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true);
{
- RID indicator_mat = VisualServer::get_singleton()->fixed_material_create();
+ indicator_mat = VisualServer::get_singleton()->fixed_material_create();
VisualServer::get_singleton()->material_set_flag( indicator_mat, VisualServer::MATERIAL_FLAG_UNSHADED, true );
VisualServer::get_singleton()->material_set_flag( indicator_mat, VisualServer::MATERIAL_FLAG_ONTOP, false );
VisualServer::get_singleton()->fixed_material_set_flag(indicator_mat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true);
@@ -2982,7 +3217,7 @@ void SpatialEditor::_init_indicators() {
d[VisualServer::ARRAY_COLOR]=origin_colors;
VisualServer::get_singleton()->mesh_add_surface(origin,VisualServer::PRIMITIVE_LINES,d);
- VisualServer::get_singleton()->mesh_surface_set_material(origin,0,indicator_mat,true);
+ VisualServer::get_singleton()->mesh_surface_set_material(origin,0,indicator_mat);
// origin = VisualServer::get_singleton()->poly_create();
@@ -3013,17 +3248,17 @@ void SpatialEditor::_init_indicators() {
cursor_points.push_back(Vector3(0,-cs,0));
cursor_points.push_back(Vector3(0,0,+cs));
cursor_points.push_back(Vector3(0,0,-cs));
- RID cmat=VisualServer::get_singleton()->fixed_material_create();
- VisualServer::get_singleton()->fixed_material_set_param(cmat,VS::FIXED_MATERIAL_PARAM_DIFFUSE,Color(0,1,1));
- VisualServer::get_singleton()->material_set_flag( cmat, VisualServer::MATERIAL_FLAG_UNSHADED, true );
- VisualServer::get_singleton()->fixed_material_set_flag(cmat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true);
- VisualServer::get_singleton()->fixed_material_set_flag(cmat, VisualServer::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true);
+ cursor_material=VisualServer::get_singleton()->fixed_material_create();
+ VisualServer::get_singleton()->fixed_material_set_param(cursor_material,VS::FIXED_MATERIAL_PARAM_DIFFUSE,Color(0,1,1));
+ VisualServer::get_singleton()->material_set_flag( cursor_material, VisualServer::MATERIAL_FLAG_UNSHADED, true );
+ VisualServer::get_singleton()->fixed_material_set_flag(cursor_material, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true);
+ VisualServer::get_singleton()->fixed_material_set_flag(cursor_material, VisualServer::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true);
Array d;
d.resize(VS::ARRAY_MAX);
d[VS::ARRAY_VERTEX]=cursor_points;
VisualServer::get_singleton()->mesh_add_surface(cursor_mesh,VS::PRIMITIVE_LINES,d);
- VisualServer::get_singleton()->mesh_surface_set_material(cursor_mesh,0,cmat,true);
+ VisualServer::get_singleton()->mesh_surface_set_material(cursor_mesh,0,cursor_material);
cursor_instance = VisualServer::get_singleton()->instance_create2(cursor_mesh,get_tree()->get_root()->get_world()->get_scenario());
VS::get_singleton()->instance_set_layer_mask(cursor_instance,1<<SpatialEditorViewport::GIZMO_GRID_LAYER);
@@ -3096,11 +3331,11 @@ void SpatialEditor::_init_indicators() {
int arrow_sides=6;
- for(int i = 0; i < 7 ; i++) {
+ for(int k = 0; k < 7 ; k++) {
- Matrix3 ma(ivec,Math_PI*2*float(i)/arrow_sides);
- Matrix3 mb(ivec,Math_PI*2*float(i+1)/arrow_sides);
+ Matrix3 ma(ivec,Math_PI*2*float(k)/arrow_sides);
+ Matrix3 mb(ivec,Math_PI*2*float(k+1)/arrow_sides);
for(int j=0;j<arrow_points-1;j++) {
@@ -3192,7 +3427,6 @@ void SpatialEditor::_init_indicators() {
void SpatialEditor::_finish_indicators() {
-
VisualServer::get_singleton()->free(origin_instance);
VisualServer::get_singleton()->free(origin);
for(int i=0;i<3;i++) {
@@ -3207,6 +3441,8 @@ void SpatialEditor::_finish_indicators() {
VisualServer::get_singleton()->free(cursor_instance);
VisualServer::get_singleton()->free(cursor_mesh);
+ VisualServer::get_singleton()->free(indicator_mat);
+ VisualServer::get_singleton()->free(cursor_material);
}
void SpatialEditor::_instance_scene() {
@@ -3295,6 +3531,7 @@ void SpatialEditor::_notification(int p_what) {
tool_button[SpatialEditor::TOOL_MODE_ROTATE]->set_icon( get_icon("ToolRotate","EditorIcons") );
tool_button[SpatialEditor::TOOL_MODE_SCALE]->set_icon( get_icon("ToolScale","EditorIcons") );
instance_button->set_icon( get_icon("SpatialAdd","EditorIcons") );
+ instance_button->hide();
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT),get_icon("Panels1","EditorIcons"));
@@ -3537,6 +3774,8 @@ void SpatialEditor::_default_light_angle_input(const InputEvent& p_event) {
SpatialEditor::SpatialEditor(EditorNode *p_editor) {
+ gizmo.visible=true;
+ gizmo.scale=1.0;
viewport_environment = Ref<Environment>( memnew( Environment ) );
undo_redo=p_editor->get_undo_redo();
@@ -3633,12 +3872,12 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
p->add_check_item("Use Default sRGB",MENU_VIEW_USE_DEFAULT_SRGB);
p->add_separator();
- p->add_check_item("1 Viewport",MENU_VIEW_USE_1_VIEWPORT,KEY_MASK_ALT+KEY_1);
- p->add_check_item("2 Viewports",MENU_VIEW_USE_2_VIEWPORTS,KEY_MASK_ALT+KEY_2);
- p->add_check_item("2 Viewports (Alt)",MENU_VIEW_USE_2_VIEWPORTS_ALT,KEY_MASK_SHIFT+KEY_MASK_ALT+KEY_2);
- p->add_check_item("3 Viewports",MENU_VIEW_USE_3_VIEWPORTS,KEY_MASK_ALT+KEY_3);
- p->add_check_item("3 Viewports (Alt)",MENU_VIEW_USE_3_VIEWPORTS_ALT,KEY_MASK_SHIFT+KEY_MASK_ALT+KEY_3);
- p->add_check_item("4 Viewports",MENU_VIEW_USE_4_VIEWPORTS,KEY_MASK_ALT+KEY_4);
+ p->add_check_item("1 Viewport",MENU_VIEW_USE_1_VIEWPORT,KEY_MASK_CMD+KEY_1);
+ p->add_check_item("2 Viewports",MENU_VIEW_USE_2_VIEWPORTS,KEY_MASK_CMD+KEY_2);
+ p->add_check_item("2 Viewports (Alt)",MENU_VIEW_USE_2_VIEWPORTS_ALT,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_2);
+ p->add_check_item("3 Viewports",MENU_VIEW_USE_3_VIEWPORTS,KEY_MASK_CMD+KEY_3);
+ p->add_check_item("3 Viewports (Alt)",MENU_VIEW_USE_3_VIEWPORTS_ALT,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_3);
+ p->add_check_item("4 Viewports",MENU_VIEW_USE_4_VIEWPORTS,KEY_MASK_CMD+KEY_4);
p->add_separator();
p->add_check_item("Display Normal",MENU_VIEW_DISPLAY_NORMAL);
@@ -3649,7 +3888,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
p->add_check_item("View Origin",MENU_VIEW_ORIGIN);
p->add_check_item("View Grid",MENU_VIEW_GRID);
p->add_separator();
- p->add_check_item("Settings",MENU_VIEW_CAMERA_SETTINGS );
+ p->add_item("Settings",MENU_VIEW_CAMERA_SETTINGS);
p->set_item_checked( p->get_item_index(MENU_VIEW_USE_DEFAULT_LIGHT), true );
@@ -3689,46 +3928,24 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
snap_dialog = memnew( ConfirmationDialog );
snap_dialog->set_title("Snap Settings");
add_child(snap_dialog);
- Label *l = memnew(Label);
- l->set_text("Translate Snap:");
- l->set_pos(Point2(5,5));
- snap_dialog->add_child(l);
+
+ VBoxContainer *snap_dialog_vbc = memnew( VBoxContainer );
+ snap_dialog->add_child(snap_dialog_vbc);
+ snap_dialog->set_child_rect(snap_dialog_vbc);
snap_translate = memnew( LineEdit );
- snap_translate->set_anchor( MARGIN_RIGHT, ANCHOR_END );
- snap_translate->set_begin( Point2(15,22) );
- snap_translate->set_end( Point2(15,35) );
snap_translate->set_text("1");
- snap_dialog->add_child(snap_translate);
-
- l = memnew(Label);
- l->set_text("Rotate Snap (deg.):");
- l->set_pos(Point2(5,45));
- snap_dialog->add_child(l);
+ snap_dialog_vbc->add_margin_child("Translate Snap:",snap_translate);
snap_rotate = memnew( LineEdit );
- snap_rotate->set_anchor( MARGIN_RIGHT, ANCHOR_END );
- snap_rotate->set_begin( Point2(15,62) );
- snap_rotate->set_end( Point2(15,75) );
snap_rotate->set_text("5");
- snap_dialog->add_child(snap_rotate);
-
-
- l = memnew(Label);
- l->set_text("Scale Snap (%):");
- l->set_pos(Point2(5,85));
- snap_dialog->add_child(l);
+ snap_dialog_vbc->add_margin_child("Rotate Snap (deg.):",snap_rotate);
snap_scale = memnew( LineEdit );
- snap_scale->set_anchor( MARGIN_RIGHT, ANCHOR_END );
- snap_scale->set_begin( Point2(15,102) );
- snap_scale->set_end( Point2(15,115) );
snap_scale->set_text("5");
- snap_dialog->add_child(snap_scale);
+ snap_dialog_vbc->add_margin_child("Scale Snap (%):",snap_scale);
- //snap_dialog->get_cancel()->hide();
-
- /* SNAP DIALOG */
+ /* SETTINGS DIALOG */
settings_dialog = memnew( ConfirmationDialog );
settings_dialog->set_title("Viewport Settings");
@@ -3802,7 +4019,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
xform_dialog = memnew( ConfirmationDialog );
xform_dialog->set_title("Transform Change");
add_child(xform_dialog);
- l = memnew(Label);
+ Label *l = memnew(Label);
l->set_text("Translate:");
l->set_pos(Point2(5,5));
xform_dialog->add_child(l);
diff --git a/tools/editor/plugins/spatial_editor_plugin.h b/tools/editor/plugins/spatial_editor_plugin.h
index 1fdc97c49d..ebd3f77fe7 100644
--- a/tools/editor/plugins/spatial_editor_plugin.h
+++ b/tools/editor/plugins/spatial_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -92,6 +92,7 @@ public:
};
private:
int index;
+ String name;
void _menu_option(int p_option);
Size2 prev_size;
@@ -110,11 +111,21 @@ private:
bool orthogonal;
float gizmo_scale;
+ struct _RayResult {
+
+ Spatial* item;
+ float depth;
+ int handle;
+ _FORCE_INLINE_ bool operator<(const _RayResult& p_rr) const { return depth<p_rr.depth; }
+ };
+
+ void _update_name();
void _compute_edit(const Point2& p_point);
void _clear_selected();
void _select_clicked(bool p_append,bool p_single);
void _select(Spatial *p_node, bool p_append,bool p_single);
ObjectID _select_ray(const Point2& p_pos, bool p_append,bool &r_includes_current,int *r_gizmo_handle=NULL,bool p_alt_select=false);
+ void _find_items_at_pos(const Point2& p_pos,bool &r_includes_current,Vector<_RayResult> &results,bool p_alt_select=false);
Vector3 _get_ray_pos(const Vector2& p_pos) const;
Vector3 _get_ray(const Vector2& p_pos);
Point2 _point_to_screen(const Vector3& p_point);
@@ -134,9 +145,12 @@ private:
float get_fov() const;
ObjectID clicked;
+ Vector<_RayResult> selection_results;
bool clicked_includes_current;
bool clicked_wants_append;
+ PopupMenu *selection_menu;
+
enum NavigationScheme {
NAVIGATION_GODOT,
NAVIGATION_MAYA,
@@ -144,6 +158,12 @@ private:
};
NavigationScheme _get_navigation_schema(const String& p_property);
+ enum NavigationZoomStyle {
+ NAVIGATION_ZOOM_VERTICAL,
+ NAVIGATION_ZOOM_HORIZONTAL
+ };
+ NavigationZoomStyle _get_navigation_zoom_style(const String& p_property);
+
enum NavigationMode {
NAVIGATION_NONE,
NAVIGATION_PAN,
@@ -216,6 +236,9 @@ private:
void _preview_exited_scene();
void _toggle_camera_preview(bool);
void _init_gizmo_instance(int p_idx);
+ void _finish_gizmo_instances();
+ void _selection_result_pressed(int);
+ void _selection_menu_hide();
protected:
@@ -230,7 +253,7 @@ public:
void set_state(const Dictionary& p_state);
Dictionary get_state() const;
void reset();
-
+ Viewport *get_viewport_node() { return viewport; }
SpatialEditorViewport(SpatialEditor *p_spatial_editor,EditorNode *p_editor,int p_index);
@@ -316,6 +339,8 @@ private:
RID indicators_instance;
RID cursor_mesh;
RID cursor_instance;
+ RID indicator_mat;
+ RID cursor_material;
/*
struct Selected {
@@ -411,6 +436,7 @@ private:
HBoxContainer *hbc_menu;
+
//
//
void _generate_selection_box();
@@ -503,6 +529,11 @@ public:
void set_can_preview(Camera* p_preview);
+ SpatialEditorViewport *get_editor_viewport(int p_idx) {
+ ERR_FAIL_INDEX_V(p_idx,4,NULL);
+ return viewports[p_idx];
+ }
+
Camera *get_camera() { return NULL; }
void edit(Spatial *p_spatial);
void clear();
diff --git a/tools/editor/plugins/sprite_frames_editor_plugin.cpp b/tools/editor/plugins/sprite_frames_editor_plugin.cpp
index e04d9dfddb..e90087efda 100644
--- a/tools/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/tools/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -76,7 +76,7 @@ void SpriteFramesEditor::_file_load_request(const DVector<String>& p_path) {
dialog->set_title("Error!");
//dialog->get_cancel()->set_text("Close");
dialog->get_ok()->set_text("Close");
- dialog->popup_centered(Size2(300,60));
+ dialog->popup_centered_minsize();
return; ///beh should show an error i guess
}
@@ -115,7 +115,7 @@ void SpriteFramesEditor::_load_pressed() {
for(int i=0;i<extensions.size();i++)
file->add_filter("*."+extensions[i]);
- file->set_mode(FileDialog::MODE_OPEN_FILES);
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILES);
file->popup_centered_ratio();
@@ -188,7 +188,7 @@ void SpriteFramesEditor::_paste_pressed() {
dialog->set_title("Error!");
//dialog->get_cancel()->set_text("Close");
dialog->get_ok()->set_text("Close");
- dialog->popup_centered(Size2(300,60));
+ dialog->popup_centered_minsize();
return; ///beh should show an error i guess
}
@@ -229,6 +229,33 @@ void SpriteFramesEditor::_empty_pressed() {
}
+void SpriteFramesEditor::_empty2_pressed() {
+
+
+ int from=-1;
+
+ if (tree->get_selected()) {
+
+ from = tree->get_selected()->get_metadata(0);
+ sel=from;
+
+ } else {
+ from=frames->get_frame_count();
+ }
+
+
+
+ Ref<Texture> r;
+
+ undo_redo->create_action("Add Empty");
+ undo_redo->add_do_method(frames,"add_frame",r,from+1);
+ undo_redo->add_undo_method(frames,"remove_frame",from+1);
+ undo_redo->add_do_method(this,"_update_library");
+ undo_redo->add_undo_method(this,"_update_library");
+ undo_redo->commit_action();
+
+}
+
void SpriteFramesEditor::_up_pressed() {
if (!tree->get_selected())
@@ -322,6 +349,8 @@ void SpriteFramesEditor::_update_library() {
ti->set_text(0,"Frame "+itos(i));
ti->set_icon(0,frames->get_frame(i));
}
+ if (frames->get_frame(i).is_valid())
+ ti->set_tooltip(0,frames->get_frame(i)->get_path());
ti->set_metadata(0,i);
ti->set_icon_max_width(0,96);
if (sel==i)
@@ -355,6 +384,7 @@ void SpriteFramesEditor::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_input_event"),&SpriteFramesEditor::_input_event);
ObjectTypeDB::bind_method(_MD("_load_pressed"),&SpriteFramesEditor::_load_pressed);
ObjectTypeDB::bind_method(_MD("_empty_pressed"),&SpriteFramesEditor::_empty_pressed);
+ ObjectTypeDB::bind_method(_MD("_empty2_pressed"),&SpriteFramesEditor::_empty2_pressed);
ObjectTypeDB::bind_method(_MD("_item_edited"),&SpriteFramesEditor::_item_edited);
ObjectTypeDB::bind_method(_MD("_delete_pressed"),&SpriteFramesEditor::_delete_pressed);
ObjectTypeDB::bind_method(_MD("_paste_pressed"),&SpriteFramesEditor::_paste_pressed);
@@ -387,9 +417,13 @@ SpriteFramesEditor::SpriteFramesEditor() {
hbc->add_child(paste);
empty = memnew( Button );
- empty->set_text("Insert Empty");
+ empty->set_text("Insert Empty (Before)");
hbc->add_child(empty);
+ empty2 = memnew( Button );
+ empty2->set_text("Insert Empty (After)");
+ hbc->add_child(empty2);
+
move_up = memnew( Button );
move_up->set_text("Up");
hbc->add_child(move_up);
@@ -401,7 +435,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
_delete = memnew( Button );
hbc->add_child(_delete);
- file = memnew( FileDialog );
+ file = memnew( EditorFileDialog );
add_child(file);
@@ -422,6 +456,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
_delete->connect("pressed", this,"_delete_pressed");
paste->connect("pressed", this,"_paste_pressed");
empty->connect("pressed", this,"_empty_pressed");
+ empty2->connect("pressed", this,"_empty2_pressed");
move_up->connect("pressed", this,"_up_pressed");
move_down->connect("pressed", this,"_down_pressed");
file->connect("files_selected", this,"_file_load_request");
diff --git a/tools/editor/plugins/sprite_frames_editor_plugin.h b/tools/editor/plugins/sprite_frames_editor_plugin.h
index 99c6ad486e..969d7b1ce3 100644
--- a/tools/editor/plugins/sprite_frames_editor_plugin.h
+++ b/tools/editor/plugins/sprite_frames_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -46,6 +46,7 @@ class SpriteFramesEditor : public PanelContainer {
Button *_delete;
Button *paste;
Button *empty;
+ Button *empty2;
Button *move_up;
Button *move_down;
Tree *tree;
@@ -53,7 +54,7 @@ class SpriteFramesEditor : public PanelContainer {
int sel;
- FileDialog *file;
+ EditorFileDialog *file;
AcceptDialog *dialog;
@@ -65,6 +66,7 @@ class SpriteFramesEditor : public PanelContainer {
void _file_load_request(const DVector<String>& p_path);
void _paste_pressed();
void _empty_pressed();
+ void _empty2_pressed();
void _delete_pressed();
void _delete_confirm_pressed();
void _up_pressed();
diff --git a/tools/editor/plugins/sprite_region_editor_plugin.cpp b/tools/editor/plugins/sprite_region_editor_plugin.cpp
new file mode 100644
index 0000000000..35c53cf562
--- /dev/null
+++ b/tools/editor/plugins/sprite_region_editor_plugin.cpp
@@ -0,0 +1,565 @@
+/*************************************************************************/
+/* sprite_region_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Author: Mariano Suligoy */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "sprite_region_editor_plugin.h"
+#include "scene/gui/check_box.h"
+#include "os/input.h"
+#include "os/keyboard.h"
+
+void SpriteRegionEditor::_region_draw()
+{
+ Ref<Texture> base_tex = node->get_texture();
+ if (base_tex.is_null())
+ return;
+
+ Matrix32 mtx;
+ mtx.elements[2]=-draw_ofs;
+ mtx.scale_basis(Vector2(draw_zoom,draw_zoom));
+
+ VS::get_singleton()->canvas_item_set_clip(edit_draw->get_canvas_item(),true);
+ VS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(),mtx);
+ edit_draw->draw_texture(base_tex,Point2());
+ VS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(),Matrix32());
+
+ if (snap_show_grid) {
+ Size2 s = edit_draw->get_size();
+ int last_cell;
+
+ if (snap_step.x!=0) {
+ for(int i=0;i<s.width;i++) {
+ int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(i,0)).x-snap_offset.x)/snap_step.x));
+ if (i==0)
+ last_cell=cell;
+ if (last_cell!=cell)
+ edit_draw->draw_line(Point2(i,0),Point2(i,s.height),Color(0.3,0.7,1,0.3));
+ last_cell=cell;
+ }
+ }
+
+ if (snap_step.y!=0) {
+ for(int i=0;i<s.height;i++) {
+ int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(0,i)).y-snap_offset.y)/snap_step.y));
+ if (i==0)
+ last_cell=cell;
+ if (last_cell!=cell)
+ edit_draw->draw_line(Point2(0,i),Point2(s.width,i),Color(0.3,0.7,1,0.3));
+ last_cell=cell;
+ }
+ }
+ }
+
+ Ref<Texture> select_handle = get_icon("EditorHandle","EditorIcons");
+
+ Rect2 scroll_rect(Point2(),mtx.basis_xform(base_tex->get_size()));
+ scroll_rect.expand_to(mtx.basis_xform(edit_draw->get_size()));
+
+ Vector2 endpoints[4]={
+ mtx.basis_xform(rect.pos),
+ mtx.basis_xform(rect.pos+Vector2(rect.size.x,0)),
+ mtx.basis_xform(rect.pos+rect.size),
+ mtx.basis_xform(rect.pos+Vector2(0,rect.size.y))
+ };
+
+ for(int i=0;i<4;i++) {
+
+ int prev = (i+3)%4;
+ int next = (i+1)%4;
+
+ Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized();
+ ofs*=1.4144*(select_handle->get_size().width/2);
+
+ edit_draw->draw_line(endpoints[i]-draw_ofs, endpoints[next]-draw_ofs, Color(0.9,0.5,0.5), 2);
+
+ edit_draw->draw_texture(select_handle,(endpoints[i]+ofs-(select_handle->get_size()/2)).floor()-draw_ofs);
+
+ ofs = (endpoints[next]-endpoints[i])/2;
+ ofs += (endpoints[next]-endpoints[i]).tangent().normalized()*(select_handle->get_size().width/2);
+
+ edit_draw->draw_texture(select_handle,(endpoints[i]+ofs-(select_handle->get_size()/2)).floor()-draw_ofs);
+
+ scroll_rect.expand_to(endpoints[i]);
+ }
+
+ scroll_rect=scroll_rect.grow(200);
+ updating_scroll=true;
+ hscroll->set_min(scroll_rect.pos.x);
+ hscroll->set_max(scroll_rect.pos.x+scroll_rect.size.x);
+ hscroll->set_page(edit_draw->get_size().x);
+ hscroll->set_val(draw_ofs.x);
+ hscroll->set_step(0.001);
+
+ vscroll->set_min(scroll_rect.pos.y);
+ vscroll->set_max(scroll_rect.pos.y+scroll_rect.size.y);
+ vscroll->set_page(edit_draw->get_size().y);
+ vscroll->set_val(draw_ofs.y);
+ vscroll->set_step(0.001);
+ updating_scroll=false;
+}
+
+void SpriteRegionEditor::_region_input(const InputEvent& p_input)
+{
+ Matrix32 mtx;
+ mtx.elements[2]=-draw_ofs;
+ mtx.scale_basis(Vector2(draw_zoom,draw_zoom));
+
+ Vector2 endpoints[8]={
+ mtx.xform(rect.pos)+Vector2(-4,-4),
+ mtx.xform(rect.pos+Vector2(rect.size.x/2,0))+Vector2(0,-4),
+ mtx.xform(rect.pos+Vector2(rect.size.x,0))+Vector2(4,-4),
+ mtx.xform(rect.pos+Vector2(rect.size.x,rect.size.y/2))+Vector2(4,0),
+ mtx.xform(rect.pos+rect.size)+Vector2(4,4),
+ mtx.xform(rect.pos+Vector2(rect.size.x/2,rect.size.y))+Vector2(0,4),
+ mtx.xform(rect.pos+Vector2(0,rect.size.y))+Vector2(-4,4),
+ mtx.xform(rect.pos+Vector2(0,rect.size.y/2))+Vector2(-4,0)
+ };
+
+ if (p_input.type==InputEvent::MOUSE_BUTTON) {
+
+
+ const InputEventMouseButton &mb=p_input.mouse_button;
+
+ if (mb.button_index==BUTTON_LEFT) {
+
+
+ if (mb.pressed) {
+
+ drag_from=mtx.affine_inverse().xform(Vector2(mb.x,mb.y));
+ drag_from=snap_point(drag_from);
+ drag=true;
+ rect_prev=node->get_region_rect();
+
+ drag_index=-1;
+ for(int i=0;i<8;i++) {
+
+ Vector2 tuv=endpoints[i];
+ if (tuv.distance_to(Vector2(mb.x,mb.y))<8) {
+ drag_index=i;
+ creating = false;
+ }
+ }
+
+ if (drag_index==-1) {
+ creating = true;
+ rect = Rect2(drag_from,Size2());
+ }
+
+ } else if (drag) {
+
+ undo_redo->create_action("Set region_rect");
+ undo_redo->add_do_method(node,"set_region_rect",node->get_region_rect());
+ undo_redo->add_undo_method(node,"set_region_rect",rect_prev);
+ undo_redo->add_do_method(edit_draw,"update");
+ undo_redo->add_undo_method(edit_draw,"update");
+ undo_redo->commit_action();
+
+ drag=false;
+ }
+
+ } else if (mb.button_index==BUTTON_RIGHT && mb.pressed) {
+
+ if (drag) {
+ drag=false;
+ node->set_region_rect(rect_prev);
+ rect=rect_prev;
+ edit_draw->update();
+ }
+
+ } else if (mb.button_index==BUTTON_WHEEL_UP && mb.pressed) {
+
+ zoom->set_val( zoom->get_val()/0.9 );
+ } else if (mb.button_index==BUTTON_WHEEL_DOWN && mb.pressed) {
+
+ zoom->set_val( zoom->get_val()*0.9);
+ }
+
+ } else if (p_input.type==InputEvent::MOUSE_MOTION) {
+
+ const InputEventMouseMotion &mm=p_input.mouse_motion;
+
+ if (mm.button_mask&BUTTON_MASK_MIDDLE || Input::get_singleton()->is_key_pressed(KEY_SPACE)) {
+
+ Vector2 draged(mm.relative_x,mm.relative_y);
+ hscroll->set_val( hscroll->get_val()-draged.x );
+ vscroll->set_val( vscroll->get_val()-draged.y );
+
+ } else if (drag) {
+
+ Vector2 new_pos = mtx.affine_inverse().xform(Vector2(mm.x,mm.y));
+ new_pos = snap_point(new_pos);
+
+ if (creating) {
+ rect = Rect2(drag_from,Size2());
+ rect.expand_to(new_pos);
+ node->set_region_rect(rect);
+ edit_draw->update();
+ return;
+ }
+
+ switch(drag_index) {
+ case 0: {
+ Vector2 p=rect_prev.pos+rect_prev.size;
+ rect = Rect2(p,Size2());
+ rect.expand_to(new_pos);
+ node->set_region_rect(rect);
+ } break;
+ case 1: {
+ Vector2 p=rect_prev.pos+Vector2(0,rect_prev.size.y);
+ rect = Rect2(p,Size2(rect_prev.size.x,0));
+ rect.expand_to(new_pos);
+ node->set_region_rect(rect);
+ } break;
+ case 2: {
+ Vector2 p=rect_prev.pos+Vector2(0,rect_prev.size.y);
+ rect = Rect2(p,Size2());
+ rect.expand_to(new_pos);
+ node->set_region_rect(rect);
+ } break;
+ case 3: {
+ Vector2 p=rect_prev.pos;
+ rect = Rect2(p,Size2(0,rect_prev.size.y));
+ rect.expand_to(new_pos);
+ node->set_region_rect(rect);
+ } break;
+ case 4: {
+ Vector2 p=rect_prev.pos;
+ rect = Rect2(p,Size2());
+ rect.expand_to(new_pos);
+ node->set_region_rect(rect);
+ } break;
+ case 5: {
+ Vector2 p=rect_prev.pos;
+ rect = Rect2(p,Size2(rect_prev.size.x,0));
+ rect.expand_to(new_pos);
+ node->set_region_rect(rect);
+ } break;
+ case 6: {
+ Vector2 p=rect_prev.pos+Vector2(rect_prev.size.x,0);
+ rect = Rect2(p,Size2());
+ rect.expand_to(new_pos);
+ node->set_region_rect(rect);
+ } break;
+ case 7: {
+ Vector2 p=rect_prev.pos+Vector2(rect_prev.size.x,0);
+ rect = Rect2(p,Size2(0,rect_prev.size.y));
+ rect.expand_to(new_pos);
+ node->set_region_rect(rect);
+ } break;
+
+ }
+ edit_draw->update();
+ }
+
+ }
+}
+
+void SpriteRegionEditor::_scroll_changed(float)
+{
+ if (updating_scroll)
+ return;
+
+ draw_ofs.x=hscroll->get_val();
+ draw_ofs.y=vscroll->get_val();
+ draw_zoom=zoom->get_val();
+ print_line("_scroll_changed");
+ edit_draw->update();
+}
+
+void SpriteRegionEditor::_set_use_snap(bool p_use)
+{
+ use_snap=p_use;
+}
+
+void SpriteRegionEditor::_set_show_grid(bool p_show)
+{
+ snap_show_grid=p_show;
+ edit_draw->update();
+}
+
+void SpriteRegionEditor::_set_snap_off_x(float p_val)
+{
+ snap_offset.x=p_val;
+ edit_draw->update();
+}
+
+void SpriteRegionEditor::_set_snap_off_y(float p_val)
+{
+ snap_offset.y=p_val;
+ edit_draw->update();
+}
+
+void SpriteRegionEditor::_set_snap_step_x(float p_val)
+{
+ snap_step.x=p_val;
+ edit_draw->update();
+}
+
+void SpriteRegionEditor::_set_snap_step_y(float p_val)
+{
+ snap_step.y=p_val;
+ edit_draw->update();
+}
+
+void SpriteRegionEditor::_notification(int p_what)
+{
+ switch(p_what) {
+
+ case NOTIFICATION_READY: {
+ edit_node->set_icon( get_icon("RegionEdit","EditorIcons"));
+ b_snap_grid->set_icon( get_icon("Grid", "EditorIcons"));
+ b_snap_enable->set_icon( get_icon("Snap", "EditorIcons"));
+ icon_zoom->set_texture( get_icon("Zoom", "EditorIcons"));
+ } break;
+ }
+}
+
+void SpriteRegionEditor::_node_removed(Node *p_node)
+{
+ if(p_node==node) {
+ node=NULL;
+ hide();
+ }
+}
+
+void SpriteRegionEditor::_bind_methods()
+{
+ ObjectTypeDB::bind_method(_MD("_edit_node"),&SpriteRegionEditor::_edit_node);
+ ObjectTypeDB::bind_method(_MD("_region_draw"),&SpriteRegionEditor::_region_draw);
+ ObjectTypeDB::bind_method(_MD("_region_input"),&SpriteRegionEditor::_region_input);
+ ObjectTypeDB::bind_method(_MD("_scroll_changed"),&SpriteRegionEditor::_scroll_changed);
+ ObjectTypeDB::bind_method(_MD("_node_removed"),&SpriteRegionEditor::_node_removed);
+ ObjectTypeDB::bind_method(_MD("_set_use_snap"),&SpriteRegionEditor::_set_use_snap);
+ ObjectTypeDB::bind_method(_MD("_set_show_grid"),&SpriteRegionEditor::_set_show_grid);
+ ObjectTypeDB::bind_method(_MD("_set_snap_off_x"),&SpriteRegionEditor::_set_snap_off_x);
+ ObjectTypeDB::bind_method(_MD("_set_snap_off_y"),&SpriteRegionEditor::_set_snap_off_y);
+ ObjectTypeDB::bind_method(_MD("_set_snap_step_x"),&SpriteRegionEditor::_set_snap_step_x);
+ ObjectTypeDB::bind_method(_MD("_set_snap_step_y"),&SpriteRegionEditor::_set_snap_step_y);
+}
+
+void SpriteRegionEditor::edit(Node *p_sprite)
+{
+ if (p_sprite) {
+ node=p_sprite->cast_to<Sprite>();
+ node->connect("exit_tree",this,"_node_removed",varray(),CONNECT_ONESHOT);
+ } else {
+ if (node)
+ node->disconnect("exit_tree",this,"_node_removed");
+ node=NULL;
+ }
+
+}
+void SpriteRegionEditor::_edit_node()
+{
+ if (node->get_texture().is_null()) {
+
+ error->set_text("No texture in this sprite.\nSet a texture to be able to edit Region.");
+ error->popup_centered_minsize();
+ return;
+ }
+
+ rect=node->get_region_rect();
+ dlg_editor->popup_centered_ratio(0.85);
+}
+
+inline float _snap_scalar(float p_offset, float p_step, float p_target) {
+ return p_step != 0 ? Math::stepify(p_target - p_offset, p_step) + p_offset : p_target;
+}
+
+Vector2 SpriteRegionEditor::snap_point(Vector2 p_target) const {
+ if (use_snap) {
+ p_target.x = _snap_scalar(snap_offset.x, snap_step.x, p_target.x);
+ p_target.y = _snap_scalar(snap_offset.y, snap_step.y, p_target.y);
+ }
+ p_target = p_target.snapped(Size2(1, 1));
+
+ return p_target;
+}
+
+SpriteRegionEditor::SpriteRegionEditor(EditorNode* p_editor)
+{
+ node=NULL;
+ editor=p_editor;
+ undo_redo = editor->get_undo_redo();
+
+ snap_step=Vector2(10,10);
+ use_snap=false;
+ snap_show_grid=false;
+
+ add_child( memnew( VSeparator ));
+ edit_node = memnew( ToolButton );
+ add_child(edit_node);
+ edit_node->connect("pressed",this,"_edit_node");
+
+ dlg_editor = memnew( AcceptDialog );
+ add_child(dlg_editor);
+ dlg_editor->set_title("Sprite Region Editor");
+ dlg_editor->set_self_opacity(0.9);
+
+ VBoxContainer *main_vb = memnew( VBoxContainer );
+ dlg_editor->add_child(main_vb);
+ dlg_editor->set_child_rect(main_vb);
+ HBoxContainer *hb_tools = memnew( HBoxContainer );
+ main_vb->add_child(hb_tools);
+
+ b_snap_enable = memnew( ToolButton );
+ hb_tools->add_child(b_snap_enable);
+ b_snap_enable->set_text("Snap");
+ b_snap_enable->set_focus_mode(FOCUS_NONE);
+ b_snap_enable->set_toggle_mode(true);
+ b_snap_enable->set_pressed(use_snap);
+ b_snap_enable->set_tooltip("Enable Snap");
+ b_snap_enable->connect("toggled",this,"_set_use_snap");
+
+ b_snap_grid = memnew( ToolButton );
+ hb_tools->add_child(b_snap_grid);
+ b_snap_grid->set_text("Grid");
+ b_snap_grid->set_focus_mode(FOCUS_NONE);
+ b_snap_grid->set_toggle_mode(true);
+ b_snap_grid->set_pressed(snap_show_grid);
+ b_snap_grid->set_tooltip("Show Grid");
+ b_snap_grid->connect("toggled",this,"_set_show_grid");
+
+ hb_tools->add_child( memnew( VSeparator ));
+ hb_tools->add_child( memnew( Label("Grid Offset:") ) );
+
+ SpinBox *sb_off_x = memnew( SpinBox );
+ sb_off_x->set_min(-256);
+ sb_off_x->set_max(256);
+ sb_off_x->set_step(1);
+ sb_off_x->set_val(snap_offset.x);
+ sb_off_x->set_suffix("px");
+ sb_off_x->connect("value_changed", this, "_set_snap_off_x");
+ hb_tools->add_child(sb_off_x);
+
+ SpinBox *sb_off_y = memnew( SpinBox );
+ sb_off_y->set_min(-256);
+ sb_off_y->set_max(256);
+ sb_off_y->set_step(1);
+ sb_off_y->set_val(snap_offset.y);
+ sb_off_y->set_suffix("px");
+ sb_off_y->connect("value_changed", this, "_set_snap_off_y");
+ hb_tools->add_child(sb_off_y);
+
+ hb_tools->add_child( memnew( VSeparator ));
+ hb_tools->add_child( memnew( Label("Grid Step:") ) );
+
+ SpinBox *sb_step_x = memnew( SpinBox );
+ sb_step_x->set_min(-256);
+ sb_step_x->set_max(256);
+ sb_step_x->set_step(1);
+ sb_step_x->set_val(snap_step.x);
+ sb_step_x->set_suffix("px");
+ sb_step_x->connect("value_changed", this, "_set_snap_step_x");
+ hb_tools->add_child(sb_step_x);
+
+ SpinBox *sb_step_y = memnew( SpinBox );
+ sb_step_y->set_min(-256);
+ sb_step_y->set_max(256);
+ sb_step_y->set_step(1);
+ sb_step_y->set_val(snap_step.y);
+ sb_step_y->set_suffix("px");
+ sb_step_y->connect("value_changed", this, "_set_snap_step_y");
+ hb_tools->add_child(sb_step_y);
+
+// MARIANOGNU::TODO: Add more tools?
+
+ HBoxContainer *main_hb = memnew( HBoxContainer );
+ main_vb->add_child(main_hb);
+ edit_draw = memnew( Control );
+ main_hb->add_child(edit_draw);
+ main_hb->set_v_size_flags(SIZE_EXPAND_FILL);
+ edit_draw->set_h_size_flags(SIZE_EXPAND_FILL);
+
+
+ hb_tools->add_child( memnew( VSeparator ));
+ icon_zoom = memnew( TextureFrame );
+ hb_tools->add_child(icon_zoom);
+
+ zoom = memnew( HSlider );
+ zoom->set_min(0.01);
+ zoom->set_max(4);
+ zoom->set_val(1);
+ zoom->set_step(0.01);
+ hb_tools->add_child(zoom);
+ zoom->set_custom_minimum_size(Size2(200,0));
+ zoom_value = memnew( SpinBox );
+ zoom->share(zoom_value);
+ zoom_value->set_custom_minimum_size(Size2(50,0));
+ hb_tools->add_child(zoom_value);
+ zoom->connect("value_changed",this,"_scroll_changed");
+
+
+
+ vscroll = memnew( VScrollBar);
+ main_hb->add_child(vscroll);
+ vscroll->connect("value_changed",this,"_scroll_changed");
+ hscroll = memnew( HScrollBar );
+ main_vb->add_child(hscroll);
+ hscroll->connect("value_changed",this,"_scroll_changed");
+
+ edit_draw->connect("draw",this,"_region_draw");
+ edit_draw->connect("input_event",this,"_region_input");
+ draw_zoom=1.0;
+ updating_scroll=false;
+
+ error = memnew( AcceptDialog);
+ add_child(error);
+
+}
+
+void SpriteRegionEditorPlugin::edit(Object *p_node)
+{
+ region_editor->edit(p_node->cast_to<Node>());
+}
+
+bool SpriteRegionEditorPlugin::handles(Object *p_node) const
+{
+ return p_node->is_type("Sprite");
+}
+
+void SpriteRegionEditorPlugin::make_visible(bool p_visible)
+{
+ if (p_visible) {
+ region_editor->show();
+ } else {
+ region_editor->hide();
+ region_editor->edit(NULL);
+ }
+}
+
+SpriteRegionEditorPlugin::SpriteRegionEditorPlugin(EditorNode *p_node)
+{
+ editor = p_node;
+ region_editor= memnew ( SpriteRegionEditor(p_node) );
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(region_editor);
+
+ region_editor->hide();
+}
+
diff --git a/tools/editor/plugins/sprite_region_editor_plugin.h b/tools/editor/plugins/sprite_region_editor_plugin.h
new file mode 100644
index 0000000000..cf69395f40
--- /dev/null
+++ b/tools/editor/plugins/sprite_region_editor_plugin.h
@@ -0,0 +1,125 @@
+/*************************************************************************/
+/* sprite_region_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Author: Mariano Suligoy */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SPRITE_REGION_EDITOR_PLUGIN_H
+#define SPRITE_REGION_EDITOR_PLUGIN_H
+
+#include "canvas_item_editor_plugin.h"
+#include "tools/editor/editor_plugin.h"
+#include "tools/editor/editor_node.h"
+#include "scene/2d/sprite.h"
+
+class SpriteRegionEditor : public HBoxContainer {
+
+ OBJ_TYPE(SpriteRegionEditor, HBoxContainer );
+
+ ToolButton *edit_node;
+// Button *use_region;
+ ToolButton *b_snap_enable;
+ ToolButton *b_snap_grid;
+ TextureFrame *icon_zoom;
+ HSlider *zoom;
+ SpinBox *zoom_value;
+ Control *edit_draw;
+
+ VScrollBar *vscroll;
+ HScrollBar *hscroll;
+
+ Sprite *node;
+ EditorNode *editor;
+ AcceptDialog *dlg_editor;
+ UndoRedo* undo_redo;
+
+ Vector2 draw_ofs;
+ float draw_zoom;
+ bool updating_scroll;
+
+ bool use_snap;
+ bool snap_show_grid;
+ Vector2 snap_offset;
+ Vector2 snap_step;
+
+ Rect2 rect;
+ Rect2 rect_prev;
+ bool drag;
+ bool creating;
+ Vector2 drag_from;
+ int drag_index;
+
+ AcceptDialog *error;
+
+ void _set_use_snap(bool p_use);
+ void _set_show_grid(bool p_show);
+ void _set_snap_off_x(float p_val);
+ void _set_snap_off_y(float p_val);
+ void _set_snap_step_x(float p_val);
+ void _set_snap_step_y(float p_val);
+
+protected:
+
+ void _notification(int p_what);
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+
+ Vector2 snap_point(Vector2 p_target) const;
+
+public:
+
+ void edit();
+ void _edit_node();
+ void _region_draw();
+ void _region_input(const InputEvent &p_input);
+ void _scroll_changed(float);
+
+ void edit(Node *p_sprite);
+ SpriteRegionEditor(EditorNode* p_editor);
+
+};
+
+class SpriteRegionEditorPlugin : public EditorPlugin
+{
+
+ OBJ_TYPE( SpriteRegionEditorPlugin, EditorPlugin );
+
+ SpriteRegionEditor *region_editor;
+ EditorNode *editor;
+public:
+
+ virtual String get_name() const { return "Sprite"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_node);
+ virtual bool handles(Object *p_node) const;
+ virtual void make_visible(bool p_visible);
+
+ SpriteRegionEditorPlugin(EditorNode *p_node);
+};
+
+#endif // SPRITE_REGION_EDITOR_PLUGIN_H
diff --git a/tools/editor/plugins/stream_editor_plugin.cpp b/tools/editor/plugins/stream_editor_plugin.cpp
index 6477cce47c..81db7f2846 100644
--- a/tools/editor/plugins/stream_editor_plugin.cpp
+++ b/tools/editor/plugins/stream_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/plugins/stream_editor_plugin.h b/tools/editor/plugins/stream_editor_plugin.h
index d49d15b765..7378bfad0c 100644
--- a/tools/editor/plugins/stream_editor_plugin.h
+++ b/tools/editor/plugins/stream_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/plugins/style_box_editor_plugin.cpp b/tools/editor/plugins/style_box_editor_plugin.cpp
index 3b537fb5c4..898c69e1e0 100644
--- a/tools/editor/plugins/style_box_editor_plugin.cpp
+++ b/tools/editor/plugins/style_box_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/plugins/style_box_editor_plugin.h b/tools/editor/plugins/style_box_editor_plugin.h
index 87f72b3cc8..00b0871572 100644
--- a/tools/editor/plugins/style_box_editor_plugin.h
+++ b/tools/editor/plugins/style_box_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/plugins/theme_editor_plugin.cpp b/tools/editor/plugins/theme_editor_plugin.cpp
index ccbd923118..63ba57bfc0 100644
--- a/tools/editor/plugins/theme_editor_plugin.cpp
+++ b/tools/editor/plugins/theme_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -408,7 +408,7 @@ void ThemeEditor::_theme_menu_cbk(int p_option) {
if (p_option==POPUP_CREATE_TEMPLATE) {
- file_dialog->set_mode(FileDialog::MODE_SAVE_FILE);
+ file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
file_dialog->set_current_path("custom.theme");
file_dialog->popup_centered_ratio();
return;
@@ -568,6 +568,24 @@ ThemeEditor::ThemeEditor() {
CheckButton *cb = memnew( CheckButton );
cb->set_text("CheckButton");
first_vb->add_child(cb );
+ CheckBox *cbx = memnew( CheckBox );
+ cbx->set_text("CheckBox");
+ first_vb->add_child(cbx );
+
+
+ ButtonGroup *bg = memnew( ButtonGroup );
+ bg->set_v_size_flags(SIZE_EXPAND_FILL);
+ VBoxContainer *gbvb = memnew( VBoxContainer );
+ gbvb->set_v_size_flags(SIZE_EXPAND_FILL);
+ CheckBox *rbx1 = memnew( CheckBox );
+ rbx1->set_text("CheckBox Radio1");
+ rbx1->set_pressed(true);
+ gbvb->add_child(rbx1);
+ CheckBox *rbx2 = memnew( CheckBox );
+ rbx2->set_text("CheckBox Radio2");
+ gbvb->add_child(rbx2);
+ bg->add_child(gbvb);
+ first_vb->add_child(bg);
MenuButton* test_menu_button = memnew( MenuButton );
test_menu_button->set_text("MenuButton");
@@ -714,7 +732,7 @@ ThemeEditor::ThemeEditor() {
fd_button->set_text("Open File Dialog");
panel->add_child(fd_button);
- test_file_dialog = memnew( FileDialog );
+ test_file_dialog = memnew( EditorFileDialog );
panel->add_child(test_file_dialog);
fd_button->connect("pressed", this,"_open_file_dialog");
@@ -784,7 +802,7 @@ ThemeEditor::ThemeEditor() {
add_del_dialog->get_ok()->connect("pressed", this,"_dialog_cbk");
- file_dialog = memnew( FileDialog );
+ file_dialog = memnew( EditorFileDialog );
file_dialog->add_filter("*.theme ; Theme File");
add_child(file_dialog);
file_dialog->connect("file_selected",this,"_save_template_cbk");
diff --git a/tools/editor/plugins/theme_editor_plugin.h b/tools/editor/plugins/theme_editor_plugin.h
index 98156422ee..40c7ad8186 100644
--- a/tools/editor/plugins/theme_editor_plugin.h
+++ b/tools/editor/plugins/theme_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,6 +33,8 @@
#include "scene/gui/texture_frame.h"
#include "scene/gui/option_button.h"
#include "scene/gui/file_dialog.h"
+#include "scene/gui/check_box.h"
+#include "scene/gui/button_group.h"
#include "tools/editor/editor_node.h"
@@ -46,7 +48,7 @@ class ThemeEditor : public Control {
VBoxContainer *main_vb;
Ref<Theme> theme;
- FileDialog *file_dialog;
+ EditorFileDialog *file_dialog;
double time_left;
diff --git a/tools/editor/plugins/tile_map_editor_plugin.cpp b/tools/editor/plugins/tile_map_editor_plugin.cpp
index a25997108b..66c7a39096 100644
--- a/tools/editor/plugins/tile_map_editor_plugin.cpp
+++ b/tools/editor/plugins/tile_map_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -34,7 +34,7 @@
#include "os/file_access.h"
#include "tools/editor/editor_settings.h"
#include "os/input.h"
-
+#include "method_bind_ext.inc"
void TileMapEditor::_notification(int p_what) {
@@ -42,9 +42,13 @@ void TileMapEditor::_notification(int p_what) {
case NOTIFICATION_READY: {
- pane_drag->connect("dragged", this,"_pane_drag");
+ transpose->set_icon( get_icon("Transpose","EditorIcons"));
mirror_x->set_icon( get_icon("MirrorX","EditorIcons"));
mirror_y->set_icon( get_icon("MirrorY","EditorIcons"));
+ rotate_0->set_icon( get_icon("Rotate0","EditorIcons"));
+ rotate_90->set_icon( get_icon("Rotate90","EditorIcons"));
+ rotate_180->set_icon( get_icon("Rotate180","EditorIcons"));
+ rotate_270->set_icon( get_icon("Rotate270","EditorIcons"));
} break;
}
@@ -67,34 +71,71 @@ void TileMapEditor::_canvas_mouse_exit() {
}
int TileMapEditor::get_selected_tile() const {
-
- TreeItem *item = palette->get_selected();
- if (!item)
+ int item = palette->get_current();
+ if (item==-1)
return TileMap::INVALID_CELL;
- return item->get_metadata(0);
+ return palette->get_item_metadata(item);
+}
+
+void TileMapEditor::set_selected_tile(int p_tile) {
+ for (int i = 0; i < palette->get_item_count(); i++) {
+ if (palette->get_item_metadata(i).operator int() == p_tile) {
+ palette->select(i,true);
+ palette->ensure_current_is_visible();
+ break;
+ }
+ }
}
-void TileMapEditor::_set_cell(const Point2i& p_pos,int p_value,bool p_flip_h, bool p_flip_v,bool p_with_undo) {
+// Wrapper to workaround five arg limit of undo/redo methods
+void TileMapEditor::_set_cell_shortened(const Point2& p_pos,int p_value,bool p_flip_h, bool p_flip_v, bool p_transpose) {
+ ERR_FAIL_COND(!node);
+ node->set_cell(floor(p_pos.x), floor(p_pos.y), p_value, p_flip_h, p_flip_v, p_transpose);
+}
+
+void TileMapEditor::_set_cell(const Point2i& p_pos,int p_value,bool p_flip_h, bool p_flip_v, bool p_transpose,bool p_with_undo) {
ERR_FAIL_COND(!node);
bool prev_flip_h=node->is_cell_x_flipped(p_pos.x,p_pos.y);
bool prev_flip_v=node->is_cell_y_flipped(p_pos.x,p_pos.y);
+ bool prev_transpose=node->is_cell_transposed(p_pos.x,p_pos.y);
int prev_val=node->get_cell(p_pos.x,p_pos.y);
- if (p_value==prev_val && p_flip_h==prev_flip_h && p_flip_v==prev_flip_v)
+ if (p_value==prev_val && p_flip_h==prev_flip_h && p_flip_v==prev_flip_v && p_transpose==prev_transpose)
return; //check that it's actually different
if (p_with_undo) {
- undo_redo->add_do_method(node,"set_cell",p_pos.x,p_pos.y,p_value,p_flip_h,p_flip_v);
- undo_redo->add_undo_method(node,"set_cell",p_pos.x,p_pos.y,prev_val,prev_flip_h,prev_flip_v);
+ undo_redo->add_do_method(node,"set_cellv",Point2(p_pos),p_value,p_flip_h,p_flip_v,p_transpose);
+ undo_redo->add_undo_method(node,"set_cellv",Point2(p_pos),prev_val,prev_flip_h,prev_flip_v,prev_transpose);
} else {
- node->set_cell(p_pos.x,p_pos.y,p_value,p_flip_h,p_flip_v);
+ node->set_cell(p_pos.x,p_pos.y,p_value,p_flip_h,p_flip_v,p_transpose);
+
+ }
+
+}
+
+void TileMapEditor::_set_display_mode(int p_mode) {
+ if (display_mode == p_mode) {
+ return;
+ }
+ switch (p_mode) {
+ case DISPLAY_THUMBNAIL: {
+ button_thumbnail->set_pressed(true);
+ button_list->set_pressed(false);
+ } break;
+ case DISPLAY_LIST: {
+ button_thumbnail->set_pressed(false);
+ button_list->set_pressed(true);
+ } break;
}
+ display_mode = p_mode;
+
+ _update_palette();
}
void TileMapEditor::_update_palette() {
@@ -102,37 +143,52 @@ void TileMapEditor::_update_palette() {
if (!node)
return;
- palette->clear();;
+ palette->clear();
Ref<TileSet> tileset=node->get_tileset();
if (!tileset.is_valid())
return;
-
- TreeItem *root = palette->create_item();
- palette->set_hide_root(true);
List<int> tiles;
tileset->get_tile_list(&tiles);
- for(List<int>::Element *E=tiles.front();E;E=E->next()) {
+ if (display_mode == DISPLAY_THUMBNAIL) {
+ palette->set_max_columns(0);
+ palette->set_icon_mode(ItemList::ICON_MODE_TOP);
+ } else if (display_mode == DISPLAY_LIST) {
+ palette->set_max_columns(1);
+ palette->set_icon_mode(ItemList::ICON_MODE_LEFT);
+ }
- TreeItem *tile = palette->create_item(root);
+ palette->set_max_text_lines(2);
+
+ for(List<int>::Element *E=tiles.front();E;E=E->next()) {
+ palette->add_item("");
- tile->set_icon_max_width(0,64);
Ref<Texture> tex = tileset->tile_get_texture(E->get());
+
if (tex.is_valid()) {
- tile->set_icon(0,tex);
Rect2 region = tileset->tile_get_region(E->get());
- if (region!=Rect2())
- tile->set_icon_region(0,region);
- } else if (tileset->tile_get_name(E->get())!="")
- tile->set_text(0,tileset->tile_get_name(E->get()));
- else
- tile->set_text(0,"#"+itos(E->get()));
+ if (!region.has_no_area()) {
+ Image data = VS::get_singleton()->texture_get_data(tex->get_rid());
+
+ Ref<ImageTexture> img = memnew( ImageTexture );
+ img->create_from_image(data.get_rect(region));
+
+ palette->set_item_icon(palette->get_item_count()-1, img);
+ } else {
+ palette->set_item_icon(palette->get_item_count()-1,tex);
+ }
+ }
- tile->set_metadata(0,E->get());
+ if (tileset->tile_get_name(E->get())!="") {
+ palette->set_item_text(palette->get_item_count()-1, tileset->tile_get_name(E->get()));
+ } else {
+ palette->set_item_text(palette->get_item_count()-1, "#"+itos(E->get()));
+ }
+ palette->set_item_metadata(palette->get_item_count()-1, E->get());
}
}
@@ -157,6 +213,7 @@ struct _TileMapEditorCopyData {
int cell;
bool flip_h;
bool flip_v;
+ bool transpose;
};
bool TileMapEditor::forward_input_event(const InputEvent& p_event) {
@@ -193,6 +250,7 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) {
tcd.cell=node->get_cell(j,i);
tcd.flip_h=node->is_cell_x_flipped(j,i);
tcd.flip_v=node->is_cell_y_flipped(j,i);
+ tcd.transpose=node->is_cell_transposed(j,i);
dupdata.push_back(tcd);
@@ -203,7 +261,7 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) {
for (List<_TileMapEditorCopyData>::Element *E=dupdata.front();E;E=E->next()) {
- _set_cell(E->get().pos+ofs,E->get().cell,E->get().flip_h,E->get().flip_v,true);
+ _set_cell(E->get().pos+ofs,E->get().cell,E->get().flip_h,E->get().flip_v,E->get().transpose,true);
}
undo_redo->commit_action();
@@ -225,28 +283,29 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) {
canvas_item_editor->update();
return true;
+ } else if (mb.mod.control) {
+ tool=TOOL_PICKING;
+ set_selected_tile(node->get_cell(over_tile.x, over_tile.y));
+ mirror_x->set_pressed(node->is_cell_x_flipped(over_tile.x, over_tile.y));
+ mirror_y->set_pressed(node->is_cell_y_flipped(over_tile.x, over_tile.y));
+ transpose->set_pressed(node->is_cell_transposed(over_tile.x, over_tile.y));
+ _update_transform_buttons();
+ canvas_item_editor->update();
+ return true;
} else {
int id = get_selected_tile();
if (id!=TileMap::INVALID_CELL) {
tool=TOOL_PAINTING;
Point2i local =node->world_to_map((xform_inv.xform(Point2(mb.x,mb.y))));
paint_undo.clear();
- CellOp op;
- op.idx = node->get_cell(local.x,local.y);
- if (op.idx>=0) {
- if (node->is_cell_x_flipped(local.x,local.y))
- op.xf=true;
- if (node->is_cell_y_flipped(local.x,local.y))
- op.yf=true;
- }
- paint_undo[local]=op;
- node->set_cell(local.x,local.y,id,mirror_x->is_pressed(),mirror_y->is_pressed());
+ paint_undo[local]=_get_op_from_cell(local);
+ node->set_cell(local.x,local.y,id,mirror_x->is_pressed(),mirror_y->is_pressed(),transpose->is_pressed());
return true;
}
}
} else {
- if (tool==TOOL_PAINTING || tool == TOOL_SELECTING) {
+ if (tool==TOOL_PAINTING || tool == TOOL_SELECTING || tool == TOOL_PICKING) {
if (tool==TOOL_PAINTING) {
@@ -255,8 +314,8 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) {
for(Map<Point2i,CellOp>::Element *E=paint_undo.front();E;E=E->next()) {
Point2i p=E->key();
- undo_redo->add_do_method(node,"set_cell",p.x,p.y,node->get_cell(p.x,p.y),node->is_cell_x_flipped(p.x,p.y),node->is_cell_y_flipped(p.x,p.y));
- undo_redo->add_undo_method(node,"set_cell",p.x,p.y,E->get().idx,E->get().xf,E->get().yf);
+ undo_redo->add_do_method(node,"set_cellv",Point2(p),node->get_cell(p.x,p.y),node->is_cell_x_flipped(p.x,p.y),node->is_cell_y_flipped(p.x,p.y),node->is_cell_transposed(p.x,p.y));
+ undo_redo->add_undo_method(node,"set_cellv",Point2(p),E->get().idx,E->get().xf,E->get().yf,E->get().tr);
}
undo_redo->commit_action();
@@ -280,20 +339,12 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) {
tool=TOOL_ERASING;
Point2i local =node->world_to_map(xform_inv.xform(Point2(mb.x,mb.y)));
paint_undo.clear();
- CellOp op;
- op.idx = node->get_cell(local.x,local.y);
- if (op.idx>=0) {
- if (node->is_cell_x_flipped(local.x,local.y))
- op.xf=true;
- if (node->is_cell_y_flipped(local.x,local.y))
- op.yf=true;
- }
- paint_undo[local]=op;
- //node->set_cell(local.x,local.y,id,mirror_x->is_pressed(),mirror_y->is_pressed());
+ paint_undo[local]=_get_op_from_cell(local);
+ //node->set_cell(local.x,local.y,id,mirror_x->is_pressed(),mirror_y->is_pressed(),transpose->is_pressed());
//return true;
_set_cell(local,TileMap::INVALID_CELL);
return true;
- } else {
+ } else if (!mb.pressed) {
if (tool==TOOL_ERASING) {
@@ -302,9 +353,10 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) {
for(Map<Point2i,CellOp>::Element *E=paint_undo.front();E;E=E->next()) {
Point2i p=E->key();
- //undo_redo->add_do_method(node,"set_cell",p.x,p.y,node->get_cell(p.x,p.y),node->is_cell_x_flipped(p.x,p.y),node->is_cell_y_flipped(p.x,p.y));
- _set_cell(p,TileMap::INVALID_CELL,false,false,true);
- undo_redo->add_undo_method(node,"set_cell",p.x,p.y,E->get().idx,E->get().xf,E->get().yf);
+ //undo_redo->add_do_method(node,"set_cell",p,node->get_cell(p.x,p.y),node->is_cell_x_flipped(p.x,p.y),node->is_cell_y_flipped(p.x,p.y),node->is_cell_transposed(p.x,p.y));
+ //_set_cell(p,TileMap::INVALID_CELL,false,false,false,true);
+ undo_redo->add_do_method(node,"set_cellv",Point2(p),TileMap::INVALID_CELL,false,false,false);
+ undo_redo->add_undo_method(node,"set_cellv",Point2(p),E->get().idx,E->get().xf,E->get().yf,E->get().tr);
}
undo_redo->commit_action();
@@ -338,17 +390,9 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) {
if (!paint_undo.has(over_tile)) {
- CellOp op;
- op.idx = node->get_cell(over_tile.x,over_tile.y);
- if (op.idx>=0) {
- if (node->is_cell_x_flipped(over_tile.x,over_tile.y))
- op.xf=true;
- if (node->is_cell_y_flipped(over_tile.x,over_tile.y))
- op.yf=true;
- }
- paint_undo[over_tile]=op;
+ paint_undo[over_tile]=_get_op_from_cell(over_tile);
}
- node->set_cell(over_tile.x,over_tile.y,id,mirror_x->is_pressed(),mirror_y->is_pressed());
+ node->set_cell(over_tile.x,over_tile.y,id,mirror_x->is_pressed(),mirror_y->is_pressed(),transpose->is_pressed());
return true;
}
@@ -375,25 +419,26 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) {
return true;
}
+
if (tool==TOOL_ERASING) {
- Point2i local =over_tile;
+ Point2i local =over_tile;
if (!paint_undo.has(over_tile)) {
-
- CellOp op;
- op.idx = node->get_cell(over_tile.x,over_tile.y);
- if (op.idx>=0) {
- if (node->is_cell_x_flipped(over_tile.x,over_tile.y))
- op.xf=true;
- if (node->is_cell_y_flipped(over_tile.x,over_tile.y))
- op.yf=true;
- }
- paint_undo[over_tile]=op;
+ paint_undo[over_tile]=_get_op_from_cell(over_tile);
}
- //node->set_cell(over_tile.x,over_tile.y,id,mirror_x->is_pressed(),mirror_y->is_pressed());
+ //node->set_cell(over_tile.x,over_tile.y,id,mirror_x->is_pressed(),mirror_y->is_pressed(),transpose->is_pressed());
_set_cell(local,TileMap::INVALID_CELL);
return true;
}
+ if (tool==TOOL_PICKING) {
+ set_selected_tile(node->get_cell(over_tile.x, over_tile.y));
+ mirror_x->set_pressed(node->is_cell_x_flipped(over_tile.x, over_tile.y));
+ mirror_y->set_pressed(node->is_cell_y_flipped(over_tile.x, over_tile.y));
+ transpose->set_pressed(node->is_cell_transposed(over_tile.x, over_tile.y));
+ _update_transform_buttons();
+ canvas_item_editor->update();
+ return true;
+ }
} break;
case InputEvent::KEY: {
@@ -629,19 +674,53 @@ void TileMapEditor::_canvas_draw() {
Ref<Texture> t = ts->tile_get_texture(st);
if (t.is_valid()) {
- Vector2 from = xform.xform(ts->tile_get_texture_offset(st)+node->map_to_world(over_tile)+node->get_cell_draw_offset());
- Rect2 r = ts->tile_get_region(st);
+ Vector2 from = node->map_to_world(over_tile)+node->get_cell_draw_offset();
+ Rect2 r = ts->tile_get_region(st);
Size2 sc = xform.get_scale();
if (mirror_x->is_pressed())
sc.x*=-1.0;
if (mirror_y->is_pressed())
sc.y*=-1.0;
+
+ Rect2 rect;
+ if (r==Rect2()) {
+ rect=Rect2(from,t->get_size());
+ } else {
+
+ rect=Rect2(from,r.get_size());
+ }
+
+
+ if (node->get_tile_origin()==TileMap::TILE_ORIGIN_TOP_LEFT) {
+ rect.pos+=ts->tile_get_texture_offset(st);
+
+ } else if (node->get_tile_origin()==TileMap::TILE_ORIGIN_CENTER) {
+ rect.pos+=node->get_cell_size()/2;
+ Vector2 s = r.size;
+
+ Vector2 center = (s/2) - ts->tile_get_texture_offset(st);
+
+
+ if (mirror_x->is_pressed())
+ rect.pos.x-=s.x-center.x;
+ else
+ rect.pos.x-=center.x;
+
+ if (mirror_y->is_pressed())
+ rect.pos.y-=s.y-center.y;
+ else
+ rect.pos.y-=center.y;
+ }
+
+ rect.pos=xform.xform(rect.pos);
+ rect.size*=sc;
+
if (r==Rect2()) {
- canvas_item_editor->draw_texture_rect(t,Rect2(from,t->get_size()*sc),false,Color(1,1,1,0.5));
+ canvas_item_editor->draw_texture_rect(t,rect,false,Color(1,1,1,0.5),transpose->is_pressed());
} else {
- canvas_item_editor->draw_texture_rect_region(t,Rect2(from,r.get_size()*sc),r,Color(1,1,1,0.5));
+ canvas_item_editor->draw_texture_rect_region(t,rect,r,Color(1,1,1,0.5),transpose->is_pressed());
}
}
}
@@ -701,27 +780,79 @@ void TileMapEditor::_tileset_settings_changed() {
canvas_item_editor->update();
}
-void TileMapEditor::_pane_drag(const Point2& p_to) {
-
- int x = theme_panel->get_margin(MARGIN_RIGHT);
-
- x+=p_to.x;
- if (x<10)
- x=10;
- if (x>300)
- x=300;
- theme_panel->set_margin(MARGIN_RIGHT,x);
-}
-
void TileMapEditor::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_menu_option"),&TileMapEditor::_menu_option);
ObjectTypeDB::bind_method(_MD("_canvas_draw"),&TileMapEditor::_canvas_draw);
- ObjectTypeDB::bind_method(_MD("_pane_drag"),&TileMapEditor::_pane_drag);
ObjectTypeDB::bind_method(_MD("_canvas_mouse_enter"),&TileMapEditor::_canvas_mouse_enter);
ObjectTypeDB::bind_method(_MD("_canvas_mouse_exit"),&TileMapEditor::_canvas_mouse_exit);
ObjectTypeDB::bind_method(_MD("_tileset_settings_changed"),&TileMapEditor::_tileset_settings_changed);
+ ObjectTypeDB::bind_method(_MD("_update_transform_buttons"),&TileMapEditor::_update_transform_buttons);
+ ObjectTypeDB::bind_method(_MD("_set_cell_shortened","pos","tile","flip_x","flip_y","transpose"),&TileMapEditor::_set_cell_shortened,DEFVAL(false),DEFVAL(false),DEFVAL(false));
+ ObjectTypeDB::bind_method(_MD("_set_display_mode","mode"),&TileMapEditor::_set_display_mode);
+}
+
+TileMapEditor::CellOp TileMapEditor::_get_op_from_cell(const Point2i& p_pos)
+{
+ CellOp op;
+ op.idx = node->get_cell(p_pos.x,p_pos.y);
+ if (op.idx>=0) {
+ if (node->is_cell_x_flipped(p_pos.x,p_pos.y))
+ op.xf=true;
+ if (node->is_cell_y_flipped(p_pos.x,p_pos.y))
+ op.yf=true;
+ if (node->is_cell_transposed(p_pos.x,p_pos.y))
+ op.tr=true;
+ }
+ return op;
+}
+
+void TileMapEditor::_update_transform_buttons(Object *p_button) {
+ //ERR_FAIL_NULL(p_button);
+ ToolButton *b=p_button->cast_to<ToolButton>();
+ //ERR_FAIL_COND(!b);
+
+ mirror_x->set_block_signals(true);
+ mirror_y->set_block_signals(true);
+ transpose->set_block_signals(true);
+ rotate_0->set_block_signals(true);
+ rotate_90->set_block_signals(true);
+ rotate_180->set_block_signals(true);
+ rotate_270->set_block_signals(true);
+
+ if (b == rotate_0) {
+ mirror_x->set_pressed(false);
+ mirror_y->set_pressed(false);
+ transpose->set_pressed(false);
+ }
+ else if (b == rotate_90) {
+ mirror_x->set_pressed(true);
+ mirror_y->set_pressed(false);
+ transpose->set_pressed(true);
+ }
+ else if (b == rotate_180) {
+ mirror_x->set_pressed(true);
+ mirror_y->set_pressed(true);
+ transpose->set_pressed(false);
+ }
+ else if (b == rotate_270) {
+ mirror_x->set_pressed(false);
+ mirror_y->set_pressed(true);
+ transpose->set_pressed(true);
+ }
+ rotate_0->set_pressed(!mirror_x->is_pressed() && !mirror_y->is_pressed() && !transpose->is_pressed());
+ rotate_90->set_pressed(mirror_x->is_pressed() && !mirror_y->is_pressed() && transpose->is_pressed());
+ rotate_180->set_pressed(mirror_x->is_pressed() && mirror_y->is_pressed() && !transpose->is_pressed());
+ rotate_270->set_pressed(!mirror_x->is_pressed() && mirror_y->is_pressed() && transpose->is_pressed());
+
+ mirror_x->set_block_signals(false);
+ mirror_y->set_block_signals(false);
+ transpose->set_block_signals(false);
+ rotate_0->set_block_signals(false);
+ rotate_90->set_block_signals(false);
+ rotate_180->set_block_signals(false);
+ rotate_270->set_block_signals(false);
}
TileMapEditor::TileMapEditor(EditorNode *p_editor) {
@@ -731,37 +862,85 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
editor=p_editor;
undo_redo = editor->get_undo_redo();
- theme_panel = memnew( Panel );
- theme_panel->set_anchor(MARGIN_BOTTOM,ANCHOR_END);
- theme_panel->set_begin( Point2(0,26));
- theme_panel->set_end( Point2(100,0) );
- p_editor->get_viewport()->add_child(theme_panel);
- theme_panel->hide();
-
- palette = memnew( Tree );
- palette->set_area_as_parent_rect(4);
- palette->set_margin(MARGIN_TOP,25);;
- theme_panel->add_child(palette);
-
- pane_drag = memnew( PaneDrag ) ;
- pane_drag->set_anchor(MARGIN_LEFT,ANCHOR_END);
- pane_drag->set_begin(Point2(16,4));
- theme_panel->add_child(pane_drag);
-
- add_child( memnew( VSeparator ));
-
+ int mw = EDITOR_DEF("tile_map/palette_min_width",80);
+ Control *ec = memnew( Control);
+ ec->set_custom_minimum_size(Size2(mw,0));
+ add_child(ec);
+
+ HBoxContainer *hb = memnew( HBoxContainer );
+ add_child(hb);
+ hb->set_h_size_flags(SIZE_EXPAND_FILL);
+ hb->add_spacer(true);
+
+ button_thumbnail = memnew( ToolButton );
+ button_thumbnail->set_toggle_mode(true);
+ button_thumbnail->set_pressed(true);
+ button_thumbnail->set_icon(p_editor->get_gui_base()->get_icon("FileThumbnail","EditorIcons"));
+ hb->add_child(button_thumbnail);
+ button_thumbnail->connect("pressed", this, "_set_display_mode", varray(DISPLAY_THUMBNAIL));
+
+ button_list = memnew( ToolButton );
+ button_list->set_toggle_mode(true);
+ button_list->set_pressed(false);
+ button_list->set_icon(p_editor->get_gui_base()->get_icon("FileList","EditorIcons"));
+ hb->add_child(button_list);
+ button_list->connect("pressed", this, "_set_display_mode", varray(DISPLAY_LIST));
+
+ // Add tile palette
+ palette = memnew( ItemList );
+ palette->set_v_size_flags(SIZE_EXPAND_FILL);
+ add_child(palette);
+
+ // Add menu items
+ canvas_item_editor_hb = memnew( HBoxContainer );
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(canvas_item_editor_hb);
+ canvas_item_editor_hb->add_child( memnew( VSeparator ));
+ transpose = memnew( ToolButton );
+ transpose->set_toggle_mode(true);
+ transpose->set_tooltip("Transpose");
+ transpose->set_focus_mode(FOCUS_NONE);
+ transpose->connect("pressed", this, "_update_transform_buttons", make_binds(transpose));
+ canvas_item_editor_hb->add_child(transpose);
mirror_x = memnew( ToolButton );
mirror_x->set_toggle_mode(true);
mirror_x->set_tooltip("Mirror X (A)");
mirror_x->set_focus_mode(FOCUS_NONE);
- add_child(mirror_x);
+ mirror_x->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_x));
+ canvas_item_editor_hb->add_child(mirror_x);
mirror_y = memnew( ToolButton );
mirror_y->set_toggle_mode(true);
mirror_y->set_tooltip("Mirror Y (S)");
mirror_y->set_focus_mode(FOCUS_NONE);
- add_child(mirror_y);
-
-
+ mirror_y->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_y));
+ canvas_item_editor_hb->add_child(mirror_y);
+ canvas_item_editor_hb->add_child(memnew(VSeparator));
+ rotate_0 = memnew( ToolButton );
+ rotate_0->set_toggle_mode(true);
+ rotate_0->set_tooltip("Rotate 0 degrees");
+ rotate_0->set_focus_mode(FOCUS_NONE);
+ rotate_0->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_0));
+ canvas_item_editor_hb->add_child(rotate_0);
+ rotate_90 = memnew( ToolButton );
+ rotate_90->set_toggle_mode(true);
+ rotate_90->set_tooltip("Rotate 90 degrees");
+ rotate_90->set_focus_mode(FOCUS_NONE);
+ rotate_90->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_90));
+ canvas_item_editor_hb->add_child(rotate_90);
+ rotate_180 = memnew( ToolButton );
+ rotate_180->set_toggle_mode(true);
+ rotate_180->set_tooltip("Rotate 180 degrees");
+ rotate_180->set_focus_mode(FOCUS_NONE);
+ rotate_180->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_180));
+ canvas_item_editor_hb->add_child(rotate_180);
+ rotate_270 = memnew( ToolButton );
+ rotate_270->set_toggle_mode(true);
+ rotate_270->set_tooltip("Rotate 270 degrees");
+ rotate_270->set_focus_mode(FOCUS_NONE);
+ rotate_270->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_270));
+ canvas_item_editor_hb->add_child(rotate_270);
+ canvas_item_editor_hb->hide();
+
+ rotate_0->set_pressed(true);
tool=TOOL_NONE;
selection_active=false;
mouse_over=false;
@@ -782,12 +961,12 @@ void TileMapEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
tile_map_editor->show();
- tile_map_editor->theme_panel->show();
+ tile_map_editor->get_canvas_item_editor_hb()->show();
} else {
tile_map_editor->hide();
- tile_map_editor->theme_panel->hide();
+ tile_map_editor->get_canvas_item_editor_hb()->hide();
tile_map_editor->edit(NULL);
}
@@ -797,7 +976,8 @@ TileMapEditorPlugin::TileMapEditorPlugin(EditorNode *p_node) {
editor=p_node;
tile_map_editor = memnew( TileMapEditor(p_node) );
- CanvasItemEditor::get_singleton()->add_control_to_menu_panel(tile_map_editor);
+ CanvasItemEditor::get_singleton()->get_palette_split()->add_child(tile_map_editor);
+ CanvasItemEditor::get_singleton()->get_palette_split()->move_child(tile_map_editor,0);
tile_map_editor->hide();
diff --git a/tools/editor/plugins/tile_map_editor_plugin.h b/tools/editor/plugins/tile_map_editor_plugin.h
index 2336507f1b..74d1573d0f 100644
--- a/tools/editor/plugins/tile_map_editor_plugin.h
+++ b/tools/editor/plugins/tile_map_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -34,15 +34,14 @@
#include "scene/2d/tile_map.h"
#include "scene/gui/tool_button.h"
#include "scene/gui/button_group.h"
-#include "tools/editor/pane_drag.h"
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
class CanvasItemEditor;
-class TileMapEditor : public HBoxContainer {
+class TileMapEditor : public VBoxContainer {
- OBJ_TYPE(TileMapEditor, BoxContainer );
+ OBJ_TYPE(TileMapEditor, VBoxContainer );
UndoRedo *undo_redo;
@@ -52,18 +51,26 @@ class TileMapEditor : public HBoxContainer {
TOOL_PAINTING,
TOOL_SELECTING,
TOOL_ERASING,
- TOOL_DUPLICATING
+ TOOL_DUPLICATING,
+ TOOL_PICKING
+ };
+
+ enum DisplayMode {
+ DISPLAY_THUMBNAIL,
+ DISPLAY_LIST
};
Tool tool;
Control *canvas_item_editor;
- Tree *palette;
+ int display_mode;
+ ItemList *palette;
+ ToolButton *button_thumbnail;
+ ToolButton *button_list;
EditorNode *editor;
Panel *panel;
TileMap *node;
MenuButton *options;
- PaneDrag *pane_drag;
bool selection_active;
Point2i selection_begin;
@@ -72,42 +79,52 @@ class TileMapEditor : public HBoxContainer {
bool mouse_over;
Label *mirror_label;
+ ToolButton *transpose;
ToolButton *mirror_x;
ToolButton *mirror_y;
+ ToolButton *rotate_0;
+ ToolButton *rotate_90;
+ ToolButton *rotate_180;
+ ToolButton *rotate_270;
+
+ HBoxContainer *canvas_item_editor_hb;
struct CellOp {
int idx;
bool xf;
bool yf;
- CellOp() { idx=-1; xf=false; yf=false; }
+ bool tr;
+ CellOp() { idx=-1; xf=false; yf=false; tr=false; }
};
Map<Point2i,CellOp> paint_undo;
int get_selected_tile() const;
+ void set_selected_tile(int p_tile);
+ void _set_display_mode(int p_mode);
void _update_palette();
- void _pane_drag(const Point2& p_to);
void _canvas_draw();
void _menu_option(int p_option);
- void _set_cell(const Point2i& p_pos, int p_value, bool p_flip_h=false, bool p_flip_v=false, bool p_with_undo=false);
+ void _set_cell(const Point2i& p_pos, int p_value, bool p_flip_h=false, bool p_flip_v=false, bool p_transpose=false, bool p_with_undo=false);
+ void _set_cell_shortened(const Point2& p_pos, int p_value, bool p_flip_h=false, bool p_flip_v=false, bool p_transpose=false);
void _canvas_mouse_enter();
void _canvas_mouse_exit();
void _tileset_settings_changed();
-friend class TileMapEditorPlugin;
- Panel *theme_panel;
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
static void _bind_methods();
+ CellOp _get_op_from_cell(const Point2i& p_pos);
+ void _update_transform_buttons(Object *p_button=0);
public:
- Vector2 snap_point(const Vector2& p_point) const;
+ HBoxContainer *get_canvas_item_editor_hb() const { return canvas_item_editor_hb; }
bool forward_input_event(const InputEvent& p_event);
void edit(Node *p_tile_map);
TileMapEditor(EditorNode *p_editor);
@@ -120,6 +137,7 @@ class TileMapEditorPlugin : public EditorPlugin {
TileMapEditor *tile_map_editor;
EditorNode *editor;
+
public:
virtual bool forward_input_event(const InputEvent& p_event) { return tile_map_editor->forward_input_event(p_event); }
diff --git a/tools/editor/plugins/tile_set_editor_plugin.cpp b/tools/editor/plugins/tile_set_editor_plugin.cpp
index a51caf7d54..09115472a8 100644
--- a/tools/editor/plugins/tile_set_editor_plugin.cpp
+++ b/tools/editor/plugins/tile_set_editor_plugin.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,83 +37,107 @@ void TileSetEditor::edit(const Ref<TileSet>& p_tileset) {
void TileSetEditor::_import_scene(Node *scene, Ref<TileSet> p_library, bool p_merge) {
if (!p_merge)
- p_library->clear();
+ p_library->clear();
for(int i=0;i<scene->get_child_count();i++) {
- Node *child = scene->get_child(i);
+ Node *child = scene->get_child(i);
- if (!child->cast_to<Sprite>()) {
- if (child->get_child_count()>0) {
- child=child->get_child(0);
- if (!child->cast_to<Sprite>()) {
- continue;
- }
+ if (!child->cast_to<Sprite>()) {
+ if (child->get_child_count()>0) {
+ child=child->get_child(0);
+ if (!child->cast_to<Sprite>()) {
+ continue;
+ }
- } else
- continue;
+ } else
+ continue;
- }
+ }
- Sprite *mi = child->cast_to<Sprite>();
- Ref<Texture> texture=mi->get_texture();
- if (texture.is_null())
+ Sprite *mi = child->cast_to<Sprite>();
+ Ref<Texture> texture=mi->get_texture();
+ Ref<CanvasItemMaterial> material=mi->get_material();
+
+ if (texture.is_null())
continue;
- int id = p_library->find_tile_by_name(mi->get_name());
- if (id<0) {
-
- id=p_library->get_last_unused_tile_id();
- p_library->create_tile(id);
- p_library->tile_set_name(id,mi->get_name());
- }
-
-
- p_library->tile_set_texture(id,texture);
- Vector2 phys_offset;
-
- if (mi->is_centered()) {
- Size2 s;
- if (mi->is_region()) {
- s=mi->get_region_rect().size;
- } else {
- s=texture->get_size();
- }
- phys_offset+=-s/2;
- }
- if (mi->is_region()) {
- p_library->tile_set_region(id,mi->get_region_rect());
- }
-
- Vector<Ref<Shape2D> >collisions;
-
- for(int j=0;j<mi->get_child_count();j++) {
-
- Node *child2 = mi->get_child(j);
- if (!child2->cast_to<StaticBody2D>())
- continue;
- StaticBody2D *sb = child2->cast_to<StaticBody2D>();
- if (sb->get_shape_count()==0)
- continue;
- Ref<Shape2D> collision=sb->get_shape(0);
- if (collision.is_valid()) {
- collisions.push_back(collision);
+ int id = p_library->find_tile_by_name(mi->get_name());
+ if (id<0) {
+
+ id=p_library->get_last_unused_tile_id();
+ p_library->create_tile(id);
+ p_library->tile_set_name(id,mi->get_name());
+ }
+
+
+
+ p_library->tile_set_texture(id,texture);
+ p_library->tile_set_material(id,material);
+
+ Vector2 phys_offset;
+
+ if (mi->is_centered()) {
+ Size2 s;
+ if (mi->is_region()) {
+ s=mi->get_region_rect().size;
+ } else {
+ s=texture->get_size();
+ }
+ phys_offset+=-s/2;
+ }
+ if (mi->is_region()) {
+ p_library->tile_set_region(id,mi->get_region_rect());
+ }
+
+ Vector<Ref<Shape2D> >collisions;
+ Ref<NavigationPolygon> nav_poly;
+ Ref<OccluderPolygon2D> occluder;
+
+ for(int j=0;j<mi->get_child_count();j++) {
+
+ Node *child2 = mi->get_child(j);
+
+ if (child2->cast_to<NavigationPolygonInstance>())
+ nav_poly=child2->cast_to<NavigationPolygonInstance>()->get_navigation_polygon();
+
+ if (child2->cast_to<LightOccluder2D>())
+ occluder=child2->cast_to<LightOccluder2D>()->get_occluder_polygon();
+
+ if (!child2->cast_to<StaticBody2D>())
+ continue;
+ StaticBody2D *sb = child2->cast_to<StaticBody2D>();
+ int shape_count = sb->get_shape_count();
+ if (shape_count==0)
+ continue;
+ for (int shape_index=0; shape_index<shape_count; ++shape_index)
+ {
+ Ref<Shape2D> collision=sb->get_shape(shape_index);
+ if (collision.is_valid()) {
+ collisions.push_back(collision);
+ }
}
- }
+ }
- if (collisions.size()) {
+ if (collisions.size()) {
- p_library->tile_set_shapes(id,collisions);
- p_library->tile_set_shape_offset(id,-phys_offset);
- } else {
- p_library->tile_set_shape_offset(id,Vector2());
+ p_library->tile_set_shapes(id,collisions);
+ p_library->tile_set_shape_offset(id,-phys_offset);
+ } else {
+ p_library->tile_set_shape_offset(id,Vector2());
+
+ }
+
+ p_library->tile_set_texture_offset(id,mi->get_offset());
+ p_library->tile_set_navigation_polygon(id,nav_poly);
+ p_library->tile_set_light_occluder(id,occluder);
+ p_library->tile_set_occluder_offset(id,-phys_offset);
+ p_library->tile_set_navigation_polygon_offset(id,-phys_offset);
- }
- p_library->tile_set_texture_offset(id,mi->get_offset());
}
}
@@ -121,23 +145,23 @@ void TileSetEditor::_menu_confirm() {
switch(option) {
- case MENU_OPTION_REMOVE_ITEM: {
+ case MENU_OPTION_REMOVE_ITEM: {
- tileset->remove_tile(to_erase);
- } break;
- case MENU_OPTION_MERGE_FROM_SCENE:
- case MENU_OPTION_CREATE_FROM_SCENE: {
+ tileset->remove_tile(to_erase);
+ } break;
+ case MENU_OPTION_MERGE_FROM_SCENE:
+ case MENU_OPTION_CREATE_FROM_SCENE: {
- EditorNode *en = editor;
- Node * scene = en->get_edited_scene();
- if (!scene)
- break;
+ EditorNode *en = editor;
+ Node * scene = en->get_edited_scene();
+ if (!scene)
+ break;
- _import_scene(scene,tileset,option==MENU_OPTION_MERGE_FROM_SCENE);
+ _import_scene(scene,tileset,option==MENU_OPTION_MERGE_FROM_SCENE);
- } break;
+ } break;
}
}
@@ -165,11 +189,11 @@ void TileSetEditor::_menu_cbk(int p_option) {
cd->set_text("Create from scene?");
cd->popup_centered(Size2(300,60));
} break;
- case MENU_OPTION_MERGE_FROM_SCENE: {
+ case MENU_OPTION_MERGE_FROM_SCENE: {
- cd->set_text("Merge from scene?");
- cd->popup_centered(Size2(300,60));
- } break;
+ cd->set_text("Merge from scene?");
+ cd->popup_centered(Size2(300,60));
+ } break;
}
}
diff --git a/tools/editor/plugins/tile_set_editor_plugin.h b/tools/editor/plugins/tile_set_editor_plugin.h
index 2531ec55bf..df82df6993 100644
--- a/tools/editor/plugins/tile_set_editor_plugin.h
+++ b/tools/editor/plugins/tile_set_editor_plugin.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -56,7 +56,7 @@ class TileSetEditor : public Control {
int option;
void _menu_cbk(int p_option);
- void _menu_confirm();
+ void _menu_confirm();
static void _import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge);
diff --git a/tools/editor/progress_dialog.cpp b/tools/editor/progress_dialog.cpp
index ac54796c64..c8b87486c0 100644
--- a/tools/editor/progress_dialog.cpp
+++ b/tools/editor/progress_dialog.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,7 +29,7 @@
#include "progress_dialog.h"
#include "main/main.h"
#include "message_queue.h"
-#include "scene/gui/empty_control.h"
+
void BackgroundProgress::_add_task(const String& p_task,const String& p_label, int p_steps) {
@@ -43,12 +43,12 @@ void BackgroundProgress::_add_task(const String& p_task,const String& p_label, i
t.progress = memnew( ProgressBar );
t.progress->set_max(p_steps);
t.progress->set_val(p_steps);
- EmptyControl *ec = memnew( EmptyControl );
+ Control *ec = memnew( Control );
ec->set_h_size_flags(SIZE_EXPAND_FILL);
ec->set_v_size_flags(SIZE_EXPAND_FILL);
t.progress->set_area_as_parent_rect();
ec->add_child(t.progress);
- ec->set_minsize(Size2(80,5));
+ ec->set_custom_minimum_size(Size2(80,5));
t.hb->add_child(ec);
add_child(t.hb);
diff --git a/tools/editor/progress_dialog.h b/tools/editor/progress_dialog.h
index a9d4938efe..7f1cc4cb2d 100644
--- a/tools/editor/progress_dialog.h
+++ b/tools/editor/progress_dialog.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/project_export.cpp b/tools/editor/project_export.cpp
index 3a0f1e76b5..cd6dc06f75 100644
--- a/tools/editor/project_export.cpp
+++ b/tools/editor/project_export.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -80,7 +80,7 @@ bool ProjectExportDialog::_create_tree(TreeItem *p_parent,EditorFileSystemDirect
String path = p_dir->get_file_path(i);
fitem->set_tooltip(0,path);
fitem->set_metadata(0,path);
- Ref<Texture> icon = get_icon( (has_icon(p_dir->get_file_type(i),"EditorIcons")?p_dir->get_file_type(i):String("Object")),"EditorIcons");
+ Ref<Texture> icon = get_icon( (has_icon(p_dir->get_file_type(i),ei)?p_dir->get_file_type(i):ot),ei);
fitem->set_icon(0,icon);
fitem->set_cell_mode(1,TreeItem::CELL_MODE_RANGE);
@@ -193,6 +193,8 @@ void ProjectExportDialog::_prop_edited(String what) {
_save_export_cfg();
+ _validate_platform();
+
}
void ProjectExportDialog::_filters_edited(String what) {
@@ -252,6 +254,13 @@ void ProjectExportDialog::_script_edited(Variant v) {
}
+void ProjectExportDialog::_sample_convert_edited(int what) {
+ EditorImportExport::get_singleton()->sample_set_action( EditorImportExport::SampleAction(sample_mode->get_selected()));
+ EditorImportExport::get_singleton()->sample_set_max_hz( sample_max_hz->get_val() );
+ EditorImportExport::get_singleton()->sample_set_trim( sample_trim->is_pressed() );
+
+}
+
void ProjectExportDialog::_notification(int p_what) {
switch(p_what) {
@@ -319,6 +328,15 @@ void ProjectExportDialog::_notification(int p_what) {
_update_group();
_update_group_tree();
+ sample_mode->select( EditorImportExport::get_singleton()->sample_get_action() );
+ sample_max_hz->set_val( EditorImportExport::get_singleton()->sample_get_max_hz() );
+ sample_trim->set_pressed( EditorImportExport::get_singleton()->sample_get_trim() );
+
+ sample_mode->connect("item_selected",this,"_sample_convert_edited");
+ sample_max_hz->connect("value_changed",this,"_sample_convert_edited");
+ sample_trim->connect("toggled",this,"_sample_convert_edited");
+
+
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -416,7 +434,7 @@ void ProjectExportDialog::_export_action(const String& p_file) {
if (FileAccess::exists(location.plus_file("engine.cfg"))) {
error->set_text("Please export outside the project folder!");
- error->popup_centered(Size2(300,70));;
+ error->popup_centered_minsize();
return;
}
String nl = (location+"/..").simplify_path();
@@ -434,7 +452,7 @@ void ProjectExportDialog::_export_action(const String& p_file) {
Error err = export_platform(platform,p_file,file_export_check->is_pressed(),file_export_password->get_text(),false);
if (err!=OK) {
error->set_text("Error exporting project!");
- error->popup_centered(Size2(300,70));;
+ error->popup_centered_minsize();
}
}
@@ -453,7 +471,7 @@ void ProjectExportDialog::_export_action_pck(const String& p_file) {
FileAccess *f = FileAccess::open(p_file,FileAccess::WRITE);
if (!f) {
error->set_text("Error exporting project PCK! Can't write");
- error->popup_centered(Size2(300,70));;
+ error->popup_centered_minsize();
}
ERR_FAIL_COND(!f);
@@ -462,7 +480,7 @@ void ProjectExportDialog::_export_action_pck(const String& p_file) {
if (err!=OK) {
error->set_text("Error exporting project!");
- error->popup_centered(Size2(300,70));;
+ error->popup_centered_minsize();
return;
}
}
@@ -478,7 +496,12 @@ Error ProjectExportDialog::export_platform(const String& p_platform, const Strin
Error err = exporter->export_project(p_path,p_debug);
if (err!=OK) {
error->set_text("Error exporting project!");
- error->popup_centered(Size2(300,70));;
+ error->popup_centered_minsize();
+ ERR_PRINT("Exporting failed!");
+ if (p_quit_after) {
+ OS::get_singleton()->set_exit_code(255);
+ get_tree()->quit();
+ }
return ERR_CANT_CREATE;
} else {
if (p_quit_after) {
@@ -507,7 +530,7 @@ void ProjectExportDialog::custom_action(const String&) {
if (exporter.is_null()) {
error->set_text("No exporter for platform '"+platform+"' yet.");
- error->popup_centered(Size2(300,70));;
+ error->popup_centered_minsize();
return;
}
@@ -558,7 +581,7 @@ void ProjectExportDialog::_update_group_list() {
TreeItem *ti = groups->create_item(r);
ti->set_text(0,E->get());
- ti->add_button(0,get_icon("Del","EditorIcons"));
+ ti->add_button(0,get_icon("Remove","EditorIcons"));
if (E->get()==current) {
ti->select(0);
}
@@ -1040,6 +1063,7 @@ void ProjectExportDialog::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_group_select_none"),&ProjectExportDialog::_group_select_none);
ObjectTypeDB::bind_method(_MD("_script_edited"),&ProjectExportDialog::_script_edited);
ObjectTypeDB::bind_method(_MD("_update_script"),&ProjectExportDialog::_update_script);
+ ObjectTypeDB::bind_method(_MD("_sample_convert_edited"),&ProjectExportDialog::_sample_convert_edited);
ObjectTypeDB::bind_method(_MD("export_platform"),&ProjectExportDialog::export_platform);
@@ -1126,7 +1150,7 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) {
tree->set_column_min_width(1,90);
filters = memnew( LineEdit );
- vb->add_margin_child("Filters for Non-Resources (Comma Separated):",filters);
+ vb->add_margin_child("Filters to export non-resource files (Comma Separated, ie: *.json, *.txt):",filters);
filters->connect("text_changed",this,"_filters_edited");
@@ -1151,7 +1175,7 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) {
image_shrink = memnew( SpinBox );
image_shrink->set_min(1);
image_shrink->set_max(8);
- image_shrink->set_step(1);
+ image_shrink->set_step(0.1);
image_vb->add_margin_child("Shrink All Images:",image_shrink);
sections->add_child(image_vb);
@@ -1211,6 +1235,7 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) {
group_image_action->add_item("Default");
group_image_action->add_item("Compress Disk");
group_image_action->add_item("Compress RAM");
+ group_image_action->add_item("Keep Original");
group_options->add_margin_child("Compress Mode:",group_image_action);
group_image_action->connect("item_selected",this,"_group_changed");
@@ -1231,7 +1256,7 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) {
group_shrink->set_min(1);
group_shrink->set_max(8);
group_shrink->set_val(1);
- group_shrink->set_step(1);
+ group_shrink->set_step(0.001);
group_options->add_margin_child("Shrink By:",group_shrink);
group_shrink->connect("value_changed",this,"_group_changed");
@@ -1240,8 +1265,8 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) {
group_options->add_child(atlas_preview);
atlas_preview->show();
atlas_preview->connect("pressed",this,"_group_atlas_preview");
- EmptyControl *ec = memnew(EmptyControl );
- ec->set_minsize(Size2(150,1));
+ Control *ec = memnew(Control );
+ ec->set_custom_minimum_size(Size2(150,1));
gvb->add_child(ec);
VBoxContainer *group_vb_right = memnew( VBoxContainer );
@@ -1310,6 +1335,22 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) {
hbc->add_child(button_reload);
*/
+
+ sample_vbox = memnew( VBoxContainer );
+ sample_vbox->set_name("Samples");
+ sections->add_child(sample_vbox);
+ sample_mode = memnew( OptionButton );
+ sample_vbox->add_margin_child("Sample Conversion Mode: (.wav files):",sample_mode);
+ sample_mode->add_item("Keep");
+ sample_mode->add_item("Compress (RAM - IMA-ADPCM)");
+ sample_max_hz = memnew( SpinBox );
+ sample_max_hz->set_max(192000);
+ sample_max_hz->set_min(8000);
+ sample_vbox->add_margin_child("Sampling Rate Limit: (hz)",sample_max_hz);
+ sample_trim = memnew( CheckButton );
+ sample_trim->set_text("Trim");
+ sample_vbox->add_margin_child("Trailing Silence:",sample_trim);
+
script_vbox = memnew( VBoxContainer );
script_vbox->set_name("Script");
sections->add_child(script_vbox);
@@ -1337,9 +1378,9 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) {
expopt="--,Export,Bundle";
- file_export = memnew( FileDialog );
+ file_export = memnew( EditorFileDialog );
add_child(file_export);
- file_export->set_access(FileDialog::ACCESS_FILESYSTEM);
+ file_export->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
file_export->set_current_dir( EditorSettings::get_singleton()->get("global/default_project_export_path") );
file_export->set_title("Export Project");
@@ -1356,8 +1397,8 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) {
file_export_password->set_editable(false);
file_export->get_vbox()->add_margin_child("Password:",file_export_password);
- pck_export = memnew( FileDialog );
- pck_export->set_access(FileDialog::ACCESS_FILESYSTEM);
+ pck_export = memnew( EditorFileDialog );
+ pck_export->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
pck_export->set_current_dir( EditorSettings::get_singleton()->get("global/default_project_export_path") );
pck_export->set_title("Export Project PCK");
pck_export->connect("file_selected", this,"_export_action_pck");
@@ -1367,6 +1408,8 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) {
button_export = add_button("Export..",!OS::get_singleton()->get_swap_ok_cancel(),"export_pck");
updating_script=false;
+ ei="EditorIcons";
+ ot="Object";
}
@@ -1648,7 +1691,7 @@ Error ProjectExport::export_project(const String& p_preset) {
if (saver.is_null()) {
memdelete(d);
- ERR_EXPLAIN("Preset '"+preset+"' references unexisting saver: "+type);
+ ERR_EXPLAIN("Preset '"+preset+"' references nonexistent saver: "+type);
ERR_FAIL_COND_V(saver.is_null(),ERR_INVALID_DATA);
}
diff --git a/tools/editor/project_export.h b/tools/editor/project_export.h
index dfe7a2d900..2f824e5ff7 100644
--- a/tools/editor/project_export.h
+++ b/tools/editor/project_export.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,14 +33,14 @@
#include "scene/gui/control.h"
#include "scene/gui/tree.h"
#include "scene/gui/label.h"
-#include "scene/gui/file_dialog.h"
+#include "tools/editor/editor_file_dialog.h"
#include "scene/gui/button.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/tab_container.h"
#include "os/dir_access.h"
#include "os/thread.h"
#include "scene/gui/option_button.h"
-#include "scene/gui/empty_control.h"
+
#include "scene/gui/slider.h"
#include "tools/editor/editor_file_system.h"
#include "property_editor.h"
@@ -78,10 +78,13 @@ private:
HBoxContainer *plat_errors;
Label *platform_error_string;
+ StringName ei;
+ StringName ot;
+
Tree * tree;
- FileDialog *pck_export;
- FileDialog *file_export;
+ EditorFileDialog *pck_export;
+ EditorFileDialog *file_export;
CheckButton *file_export_check;
LineEdit *file_export_password;
@@ -136,6 +139,10 @@ private:
OptionButton *script_mode;
LineEdit *script_key;
+ VBoxContainer *sample_vbox;
+ OptionButton *sample_mode;
+ SpinBox *sample_max_hz;
+ CheckButton *sample_trim;
void _export_mode_changed(int p_idx);
@@ -159,6 +166,8 @@ private:
void _image_export_edited(int what);
void _shrink_edited(float what);
+ void _sample_convert_edited(int what);
+
void _update_group_list();
void _select_group(const String& p_by_name);
diff --git a/tools/editor/project_manager.cpp b/tools/editor/project_manager.cpp
index 4eadd980f4..893df04709 100644
--- a/tools/editor/project_manager.cpp
+++ b/tools/editor/project_manager.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -39,7 +39,7 @@
#include "scene/gui/line_edit.h"
#include "scene/gui/panel_container.h"
-#include "scene/gui/empty_control.h"
+
#include "scene/gui/texture_frame.h"
#include "scene/gui/margin_container.h"
#include "io/resource_saver.h"
@@ -65,7 +65,7 @@ class NewProjectDialog : public ConfirmationDialog {
error->set_text("");
get_ok()->set_disabled(true);
DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- if (d->change_dir(project_path->get_text())!=OK) {
+ if (project_path->get_text() != "" && d->change_dir(project_path->get_text())!=OK) {
error->set_text("Invalid Path for Project, Path Must Exist!");
memdelete(d);
return false;
@@ -82,7 +82,7 @@ class NewProjectDialog : public ConfirmationDialog {
} else {
- if (!d->file_exists("engine.cfg")) {
+ if (project_path->get_text() != "" && !d->file_exists("engine.cfg")) {
error->set_text("Invalid Project Path (engine.cfg must exist).");
memdelete(d);
@@ -144,7 +144,7 @@ class NewProjectDialog : public ConfirmationDialog {
fdialog->set_mode(FileDialog::MODE_OPEN_FILE);
fdialog->clear_filters();
- fdialog->add_filter("engine.cfg ; "_MKSTR(VERSION_NAME)" Project");
+ fdialog->add_filter("engine.cfg ; " _MKSTR(VERSION_NAME) " Project");
} else {
fdialog->set_mode(FileDialog::MODE_OPEN_DIR);
}
@@ -193,7 +193,7 @@ class NewProjectDialog : public ConfirmationDialog {
f->store_line("\n");
f->store_line("[application]");
f->store_line("name=\""+project_name->get_text()+"\"");
- f->store_line("icon=\"icon.png\"");
+ f->store_line("icon=\"res://icon.png\"");
memdelete(f);
@@ -245,7 +245,8 @@ public:
project_name->clear();
if (import_mode) {
- set_title("Import Existing Project:");
+ set_title("Import Existing Project");
+ get_ok()->set_text("Import");
pp->set_text("Project Path: (Must exist)");
pn->set_text("Project Name:");
pn->hide();
@@ -254,7 +255,8 @@ public:
popup_centered(Size2(500,125));
} else {
- set_title("Create New Project:");
+ set_title("Create New Project");
+ get_ok()->set_text("Create");
pp->set_text("Project Path:");
pn->set_text("Project Name:");
pn->show();
@@ -313,7 +315,6 @@ public:
l->add_color_override("font_color",Color(1,0.4,0.3,0.8));
l->set_align(Label::ALIGN_CENTER);
- get_ok()->set_text("Create");
DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
project_path->set_text(d->get_current_dir());
memdelete(d);
@@ -479,20 +480,25 @@ void ProjectManager::_load_recent_projects() {
bool favorite = (_name.begins_with("favorite_projects/"))?true:false;
uint64_t last_modified = 0;
- if (FileAccess::exists(conf))
+ if (FileAccess::exists(conf)) {
last_modified = FileAccess::get_modified_time(conf);
- String fscache = path.plus_file(".fscache");
- if (FileAccess::exists(fscache)) {
- uint64_t cache_modified = FileAccess::get_modified_time(fscache);
- if ( cache_modified > last_modified )
- last_modified = cache_modified;
- }
- ProjectItem item(project, path, conf, last_modified, favorite);
- if (favorite)
- favorite_projects.push_back(item);
- else
- projects.push_back(item);
+ String fscache = path.plus_file(".fscache");
+ if (FileAccess::exists(fscache)) {
+ uint64_t cache_modified = FileAccess::get_modified_time(fscache);
+ if ( cache_modified > last_modified )
+ last_modified = cache_modified;
+ }
+
+ ProjectItem item(project, path, conf, last_modified, favorite);
+ if (favorite)
+ favorite_projects.push_back(item);
+ else
+ projects.push_back(item);
+ } else {
+ //project doesn't exist on disk but it's in the XML settings file
+ EditorSettings::get_singleton()->erase(_name); //remove it
+ }
}
projects.sort();
@@ -580,8 +586,8 @@ void ProjectManager::_load_recent_projects() {
VBoxContainer *vb = memnew(VBoxContainer);
hb->add_child(vb);
- EmptyControl *ec = memnew( EmptyControl );
- ec->set_minsize(Size2(0,1));
+ Control *ec = memnew( Control );
+ ec->set_custom_minimum_size(Size2(0,1));
vb->add_child(ec);
Label *title = memnew( Label(project_name) );
title->add_font_override("font",get_font("large","Fonts"));
@@ -600,6 +606,8 @@ void ProjectManager::_load_recent_projects() {
erase_btn->set_disabled(selected_list.size()<1);
open_btn->set_disabled(selected_list.size()<1);
run_btn->set_disabled(selected_list.size()<1 || (selected_list.size()==1 && single_selected_main==""));
+
+ EditorSettings::get_singleton()->save();
}
void ProjectManager::_open_project_confirm() {
@@ -639,7 +647,7 @@ void ProjectManager::_open_project() {
if (selected_list.size()>1) {
multi_open_ask->set_text("Are you sure to open more than one projects?");
- multi_open_ask->popup_centered(Size2(300,100));
+ multi_open_ask->popup_centered_minsize();
} else {
_open_project_confirm();
}
@@ -679,7 +687,7 @@ void ProjectManager::_run_project() {
if (selected_list.size()>1) {
multi_run_ask->set_text("Are you sure to run more than one projects?");
- multi_run_ask->popup_centered(Size2(300,100));
+ multi_run_ask->popup_centered_minsize();
} else {
_run_project_confirm();
}
@@ -779,7 +787,7 @@ void ProjectManager::_erase_project() {
erase_ask->set_text("Erase project from list?? (Folder contents will not be modified)");
- erase_ask->popup_centered(Size2(300,100));
+ erase_ask->popup_centered_minsize();
}
@@ -819,6 +827,7 @@ ProjectManager::ProjectManager() {
if (!EditorSettings::get_singleton())
EditorSettings::create();
+ FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("file_dialog/show_hidden_files"));
set_area_as_parent_rect();
Panel *panel = memnew( Panel );
@@ -837,7 +846,7 @@ ProjectManager::ProjectManager() {
l->set_align(Label::ALIGN_CENTER);
vb->add_child(l);
l = memnew( Label );
- l->set_text("v"VERSION_MKSTRING);
+ l->set_text("v" VERSION_MKSTRING);
//l->add_font_override("font",get_font("bold","Fonts"));
l->set_align(Label::ALIGN_CENTER);
vb->add_child(l);
@@ -940,7 +949,7 @@ ProjectManager::ProjectManager() {
String cp;
cp.push_back(0xA9);
cp.push_back(0);
- l->set_text(cp+" 2008-2014 Juan Linietsky, Ariel Manzur.");
+ l->set_text(cp+" 2008-2015 Juan Linietsky, Ariel Manzur.");
l->set_align(Label::ALIGN_CENTER);
vb->add_child(l);
diff --git a/tools/editor/project_manager.h b/tools/editor/project_manager.h
index c51a885d7d..1e6ea9c1c9 100644
--- a/tools/editor/project_manager.h
+++ b/tools/editor/project_manager.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/project_settings.cpp b/tools/editor/project_settings.cpp
index 304fb76d1c..2fd8b37753 100644
--- a/tools/editor/project_settings.cpp
+++ b/tools/editor/project_settings.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -59,6 +59,9 @@ void ProjectSettings::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_TREE) {
+ search_button->set_icon(get_icon("Zoom","EditorIcons"));
+ clear_button->set_icon(get_icon("Close","EditorIcons"));
+
translation_list->connect("button_pressed",this,"_translation_delete");
_update_actions();
popup_add->add_icon_item(get_icon("Keyboard","EditorIcons"),"Key",InputEvent::KEY);
@@ -415,7 +418,7 @@ void ProjectSettings::_update_actions() {
item->set_cell_mode(0,TreeItem::CELL_MODE_CHECK);
item->set_text(0,name);
item->add_button(0,get_icon("Add","EditorIcons"),1);
- item->add_button(0,get_icon("Del","EditorIcons"),2);
+ item->add_button(0,get_icon("Remove","EditorIcons"),2);
item->set_custom_bg_color(0,get_color("prop_subsection","Editor"));
item->set_editable(0,true);
item->set_checked(0,pi.usage&PROPERTY_USAGE_CHECKED);
@@ -483,7 +486,7 @@ void ProjectSettings::_update_actions() {
action->set_icon(0,get_icon("JoyAxis","EditorIcons"));
} break;
}
- action->add_button(0,get_icon("Del","EditorIcons"),2);
+ action->add_button(0,get_icon("Remove","EditorIcons"),2);
action->set_metadata(0,i);
}
}
@@ -771,18 +774,47 @@ void ProjectSettings::_autoload_add() {
void ProjectSettings::_autoload_delete(Object *p_item,int p_column, int p_button) {
+
TreeItem *ti=p_item->cast_to<TreeItem>();
String name = "autoload/"+ti->get_text(0);
- undo_redo->create_action("Remove Autoload");
- undo_redo->add_do_property(Globals::get_singleton(),name,Variant());
- undo_redo->add_undo_property(Globals::get_singleton(),name,Globals::get_singleton()->get(name));
- undo_redo->add_undo_method(Globals::get_singleton(),"set_persisting",name,true);
- undo_redo->add_do_method(this,"_update_autoload");
- undo_redo->add_undo_method(this,"_update_autoload");
- undo_redo->add_do_method(this,"_settings_changed");
- undo_redo->add_undo_method(this,"_settings_changed");
- undo_redo->commit_action();
+ if (p_button==0) {
+ //delete
+ undo_redo->create_action("Remove Autoload");
+ undo_redo->add_do_property(Globals::get_singleton(),name,Variant());
+ undo_redo->add_undo_property(Globals::get_singleton(),name,Globals::get_singleton()->get(name));
+ undo_redo->add_undo_method(Globals::get_singleton(),"set_persisting",name,true);
+ undo_redo->add_do_method(this,"_update_autoload");
+ undo_redo->add_undo_method(this,"_update_autoload");
+ undo_redo->add_do_method(this,"_settings_changed");
+ undo_redo->add_undo_method(this,"_settings_changed");
+ undo_redo->commit_action();
+ } else {
+
+ TreeItem *swap;
+
+ if (p_button==1) {
+ swap=ti->get_prev();
+ } else if (p_button==2) {
+ swap=ti->get_next();
+ }
+ if (!swap)
+ return;
+
+ String swap_name= "autoload/"+swap->get_text(0);
+
+ undo_redo->create_action("Move Autoload");
+ undo_redo->add_do_method(Globals::get_singleton(),"set_order",swap_name,Globals::get_singleton()->get_order(name));
+ undo_redo->add_do_method(Globals::get_singleton(),"set_order",name,Globals::get_singleton()->get_order(swap_name));
+ undo_redo->add_undo_method(Globals::get_singleton(),"set_order",swap_name,Globals::get_singleton()->get_order(swap_name));
+ undo_redo->add_undo_method(Globals::get_singleton(),"set_order",name,Globals::get_singleton()->get_order(name));
+ undo_redo->add_do_method(this,"_update_autoload");
+ undo_redo->add_undo_method(this,"_update_autoload");
+ undo_redo->add_do_method(this,"_settings_changed");
+ undo_redo->add_undo_method(this,"_settings_changed");
+ undo_redo->commit_action();
+
+ }
}
@@ -916,8 +948,8 @@ void ProjectSettings::_translation_res_option_changed() {
ERR_FAIL_COND(!remaps.has(key));
- StringArray r = remaps[key];
- ERR_FAIL_INDEX(idx,remaps.size());
+ StringArray r = remaps[key];
+ ERR_FAIL_INDEX(idx,r.size());
r.set(idx,path+":"+langs[which]);
remaps[key]=r;
@@ -1095,6 +1127,7 @@ void ProjectSettings::_update_translations() {
t2->set_editable(1,true);
t2->set_metadata(1,path);
int idx = langs.find(locale);
+ print_line("find "+locale+" at "+itos(idx));
if (idx<0)
idx=0;
@@ -1133,12 +1166,39 @@ void ProjectSettings::_update_autoload() {
TreeItem *t = autoload_list->create_item(root);
t->set_text(0,name);
t->set_text(1,Globals::get_singleton()->get(pi.name));
+ t->add_button(1,get_icon("MoveUp","EditorIcons"),1);
+ t->add_button(1,get_icon("MoveDown","EditorIcons"),2);
t->add_button(1,get_icon("Del","EditorIcons"),0);
}
}
+void ProjectSettings::_toggle_search_bar(bool p_pressed) {
+
+ globals_editor->set_use_filter(p_pressed);
+
+ if (p_pressed) {
+
+ search_bar->show();
+ add_prop_bar->hide();
+ search_box->grab_focus();
+ search_box->select_all();
+ } else {
+
+ search_bar->hide();
+ add_prop_bar->show();
+ }
+}
+
+void ProjectSettings::_clear_search_box() {
+
+ if (search_box->get_text()=="")
+ return;
+
+ search_box->clear();
+ globals_editor->update_tree();
+}
void ProjectSettings::_bind_methods() {
@@ -1180,6 +1240,9 @@ void ProjectSettings::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_update_autoload"),&ProjectSettings::_update_autoload);
ObjectTypeDB::bind_method(_MD("_autoload_delete"),&ProjectSettings::_autoload_delete);
+ ObjectTypeDB::bind_method(_MD("_clear_search_box"),&ProjectSettings::_clear_search_box);
+ ObjectTypeDB::bind_method(_MD("_toggle_search_bar"),&ProjectSettings::_toggle_search_bar);
+
}
ProjectSettings::ProjectSettings(EditorData *p_data) {
@@ -1200,88 +1263,96 @@ ProjectSettings::ProjectSettings(EditorData *p_data) {
//tab_container->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN, 15 );
//tab_container->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END, 35 );
- Control *props_base = memnew( Control );
+ VBoxContainer *props_base = memnew( VBoxContainer );
+ props_base->set_alignment(BoxContainer::ALIGN_BEGIN);
+ props_base->set_v_size_flags(Control::SIZE_EXPAND_FILL);
tab_container->add_child(props_base);
props_base->set_name("General");
- globals_editor = memnew( PropertyEditor );
- props_base->add_child(globals_editor);
- globals_editor->set_area_as_parent_rect();
- globals_editor->hide_top_label();
- globals_editor->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN, 55 );
- globals_editor->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END, 35 );
- globals_editor->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN, 5 );
- globals_editor->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END, 5 );
- globals_editor->set_capitalize_paths(false);
- globals_editor->get_scene_tree()->connect("cell_selected",this,"_item_selected");
- globals_editor->connect("property_toggled",this,"_item_checked");
- globals_editor->connect("property_edited",this,"_settings_prop_edited");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ props_base->add_child(hbc);
- Label *l = memnew( Label );
- props_base->add_child(l);
- l->set_pos(Point2(6,5));
- l->set_text("Category:");
+ search_button = memnew( ToolButton );
+ search_button->set_toggle_mode(true);
+ search_button->set_pressed(false);
+ search_button->set_text("Search");
+ hbc->add_child(search_button);
+ search_button->connect("toggled",this,"_toggle_search_bar");
+ hbc->add_child( memnew( VSeparator ) );
- l = memnew( Label );
- l->set_anchor(MARGIN_LEFT,ANCHOR_RATIO);
- props_base->add_child(l);
- l->set_begin(Point2(0.21,5));
- l->set_text("Property:");
+ add_prop_bar = memnew( HBoxContainer );
+ add_prop_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ hbc->add_child(add_prop_bar);
- l = memnew( Label );
- l->set_anchor(MARGIN_LEFT,ANCHOR_RATIO);
- props_base->add_child(l);
- l->set_begin(Point2(0.51,5));
- l->set_text("Type:");
+ Label *l = memnew( Label );
+ add_prop_bar->add_child(l);
+ l->set_text("Category:");
category = memnew( LineEdit );
- props_base->add_child(category);
- category->set_anchor(MARGIN_RIGHT,ANCHOR_RATIO);
- category->set_begin( Point2(5,25) );
- category->set_end( Point2(0.20,26) );
+ category->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ add_prop_bar->add_child(category);
category->connect("text_entered",this,"_item_adds");
+ l = memnew( Label );
+ add_prop_bar->add_child(l);
+ l->set_text("Property:");
+
property = memnew( LineEdit );
- props_base->add_child(property);
- property->set_anchor(MARGIN_LEFT,ANCHOR_RATIO);
- property->set_anchor(MARGIN_RIGHT,ANCHOR_RATIO);
- property->set_begin( Point2(0.21,25) );
- property->set_end( Point2(0.50,26) );
+ property->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ add_prop_bar->add_child(property);
property->connect("text_entered",this,"_item_adds");
+ l = memnew( Label );
+ add_prop_bar->add_child(l);
+ l->set_text("Type:");
type = memnew( OptionButton );
- props_base->add_child(type);
- type->set_anchor(MARGIN_LEFT,ANCHOR_RATIO);
- type->set_anchor(MARGIN_RIGHT,ANCHOR_RATIO);
- type->set_begin( Point2(0.51,25) );
- type->set_end( Point2(0.70,26) );
+ type->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ add_prop_bar->add_child(type);
type->add_item("bool");
type->add_item("int");
type->add_item("float");
type->add_item("string");
Button *add = memnew( Button );
- props_base->add_child(add);
- add->set_anchor(MARGIN_LEFT,ANCHOR_RATIO);
- add->set_anchor(MARGIN_RIGHT,ANCHOR_RATIO);
- add->set_begin( Point2(0.71,25) );
- add->set_end( Point2(0.85,26) );
+ add_prop_bar->add_child(add);
add->set_text("Add");
add->connect("pressed",this,"_item_add");
Button *del = memnew( Button );
- props_base->add_child(del);
- del->set_anchor(MARGIN_LEFT,ANCHOR_RATIO);
- del->set_anchor(MARGIN_RIGHT,ANCHOR_END);
- del->set_begin( Point2(0.86,25) );
- del->set_end( Point2(5,26) );
+ add_prop_bar->add_child(del);
del->set_text("Del");
del->connect("pressed",this,"_item_del");
+ search_bar = memnew( HBoxContainer );
+ search_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ hbc->add_child(search_bar);
+ search_bar->hide();
+
+ search_box = memnew( LineEdit );
+ search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ search_bar->add_child(search_box);
+
+ clear_button = memnew( ToolButton );
+ search_bar->add_child(clear_button);
+ clear_button->connect("pressed",this,"_clear_search_box");
+
+ globals_editor = memnew( PropertyEditor );
+ props_base->add_child(globals_editor);
+ globals_editor->hide_top_label();
+ globals_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ globals_editor->register_text_enter(search_box);
+ globals_editor->set_capitalize_paths(false);
+ globals_editor->get_scene_tree()->connect("cell_selected",this,"_item_selected");
+ globals_editor->connect("property_toggled",this,"_item_checked");
+ globals_editor->connect("property_edited",this,"_settings_prop_edited");
+
+/*
Button *save = memnew( Button );
- //props_base->add_child(save);
+ props_base->add_child(save);
+
save->set_anchor(MARGIN_LEFT,ANCHOR_END);
save->set_anchor(MARGIN_RIGHT,ANCHOR_END);
save->set_anchor(MARGIN_TOP,ANCHOR_END);
@@ -1290,18 +1361,17 @@ ProjectSettings::ProjectSettings(EditorData *p_data) {
save->set_end( Point2(10,20) );
save->set_text("Save");
save->connect("pressed",this,"_save");
+*/
+
+ hbc = memnew( HBoxContainer );
+ props_base->add_child(hbc);
popup_platform = memnew( MenuButton );
popup_platform->set_text("Copy To Platform..");
popup_platform->set_disabled(true);
- props_base->add_child(popup_platform);
+ hbc->add_child(popup_platform);
- popup_platform->set_anchor(MARGIN_LEFT,ANCHOR_BEGIN);
- popup_platform->set_anchor(MARGIN_RIGHT,ANCHOR_BEGIN);
- popup_platform->set_anchor(MARGIN_TOP,ANCHOR_END);
- popup_platform->set_anchor(MARGIN_BOTTOM,ANCHOR_END);
- popup_platform->set_begin( Point2(10,28) );
- popup_platform->set_end( Point2(150,20) );
+ hbc->add_spacer();
List<StringName> ep;
EditorImportExport::get_singleton()->get_export_platforms(&ep);
@@ -1409,6 +1479,7 @@ ProjectSettings::ProjectSettings(EditorData *p_data) {
device_input->add_child(device_index);
+ /*
save = memnew( Button );
input_base->add_child(save);
save->set_anchor(MARGIN_LEFT,ANCHOR_END);
@@ -1419,7 +1490,7 @@ ProjectSettings::ProjectSettings(EditorData *p_data) {
save->set_end( Point2(10,20) );
save->set_text("Save");
save->connect("pressed",this,"_save");
-
+*/
setting=false;
//translations
@@ -1446,9 +1517,9 @@ ProjectSettings::ProjectSettings(EditorData *p_data) {
translation_list->set_v_size_flags(SIZE_EXPAND_FILL);
tmc->add_child(translation_list);
- translation_file_open=memnew( FileDialog );
+ translation_file_open=memnew( EditorFileDialog );
add_child(translation_file_open);
- translation_file_open->set_mode(FileDialog::MODE_OPEN_FILE);
+ translation_file_open->set_mode(EditorFileDialog::MODE_OPEN_FILE);
translation_file_open->connect("file_selected",this,"_translation_add");
}
@@ -1473,9 +1544,9 @@ ProjectSettings::ProjectSettings(EditorData *p_data) {
tmc->add_child(translation_remap);
translation_remap->connect("button_pressed",this,"_translation_res_delete");
- translation_res_file_open=memnew( FileDialog );
+ translation_res_file_open=memnew( EditorFileDialog );
add_child(translation_res_file_open);
- translation_res_file_open->set_mode(FileDialog::MODE_OPEN_FILE);
+ translation_res_file_open->set_mode(EditorFileDialog::MODE_OPEN_FILE);
translation_res_file_open->connect("file_selected",this,"_translation_res_add");
thb = memnew( HBoxContainer);
@@ -1503,9 +1574,9 @@ ProjectSettings::ProjectSettings(EditorData *p_data) {
translation_remap_options->connect("item_edited",this,"_translation_res_option_changed");
translation_remap_options->connect("button_pressed",this,"_translation_res_option_delete");
- translation_res_option_file_open=memnew( FileDialog );
+ translation_res_option_file_open=memnew( EditorFileDialog );
add_child(translation_res_option_file_open);
- translation_res_option_file_open->set_mode(FileDialog::MODE_OPEN_FILE);
+ translation_res_option_file_open->set_mode(EditorFileDialog::MODE_OPEN_FILE);
translation_res_option_file_open->connect("file_selected",this,"_translation_res_option_add");
}
@@ -1544,9 +1615,9 @@ ProjectSettings::ProjectSettings(EditorData *p_data) {
autoload_list->set_v_size_flags(SIZE_EXPAND_FILL);
avb->add_margin_child("List:",autoload_list,true);
- autoload_file_open=memnew( FileDialog );
+ autoload_file_open=memnew( EditorFileDialog );
add_child(autoload_file_open);
- autoload_file_open->set_mode(FileDialog::MODE_OPEN_FILE);
+ autoload_file_open->set_mode(EditorFileDialog::MODE_OPEN_FILE);
autoload_file_open->connect("file_selected",this,"_autoload_file_callback");
autoload_list->set_columns(2);
diff --git a/tools/editor/project_settings.h b/tools/editor/project_settings.h
index 51f49a46ff..b122609e52 100644
--- a/tools/editor/project_settings.h
+++ b/tools/editor/project_settings.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -47,6 +47,12 @@ class ProjectSettings : public AcceptDialog {
UndoRedo *undo_redo;
PropertyEditor *globals_editor;
+ HBoxContainer *search_bar;
+ ToolButton *search_button;
+ LineEdit *search_box;
+ ToolButton *clear_button;
+
+ HBoxContainer *add_prop_bar;
ConfirmationDialog *message;
LineEdit *category;
LineEdit *property;
@@ -67,19 +73,19 @@ class ProjectSettings : public AcceptDialog {
InputEvent last_wait_for_key;
- FileDialog *translation_file_open;
+ EditorFileDialog *translation_file_open;
Tree *translation_list;
Button *translation_res_option_add_button;
- FileDialog *translation_res_file_open;
- FileDialog *translation_res_option_file_open;
+ EditorFileDialog *translation_res_file_open;
+ EditorFileDialog *translation_res_option_file_open;
Tree *translation_remap;
Tree *translation_remap_options;
Tree *autoload_list;
- FileDialog *autoload_file_open;
+ EditorFileDialog *autoload_file_open;
LineEdit *autoload_add_name;
LineEdit *autoload_add_path;
@@ -130,6 +136,9 @@ class ProjectSettings : public AcceptDialog {
void _translation_res_option_changed();
void _translation_res_option_delete(Object *p_item,int p_column, int p_button);
+ void _toggle_search_bar(bool p_pressed);
+ void _clear_search_box();
+
ProjectSettings();
diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp
index 777694481b..31393ebcbc 100644
--- a/tools/editor/property_editor.cpp
+++ b/tools/editor/property_editor.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -39,17 +39,22 @@
#include "editor_settings.h"
#include "editor_import_export.h"
#include "editor_node.h"
+#include "multi_node_edit.h"
+#include "array_property_edit.h"
+#include "editor_help.h"
+#include "scene/resources/packed_scene.h"
+
void CustomPropertyEditor::_notification(int p_what) {
-
+
if (p_what==NOTIFICATION_DRAW) {
-
+
RID ci = get_canvas_item();
- get_stylebox("panel","PopupMenu")->draw(ci,Rect2(Point2(),get_size()));
+ get_stylebox("panel","PopupMenu")->draw(ci,Rect2(Point2(),get_size()));
/*
if (v.get_type()==Variant::COLOR) {
-
+
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2( 10,10,60, get_size().height-20 ), v );
}*/
}
@@ -83,7 +88,7 @@ void CustomPropertyEditor::_menu_option(int p_which) {
switch(p_which) {
case OBJ_MENU_LOAD: {
- file->set_mode(FileDialog::MODE_OPEN_FILE);
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
List<String> extensions;
String type=(hint==PROPERTY_HINT_RESOURCE_TYPE)?hint_text:String();
@@ -142,7 +147,7 @@ void CustomPropertyEditor::_menu_option(int p_which) {
}
String orig_type = res_orig->get_type();
- print_line("orig type: "+orig_type);
+
Object *inst = ObjectTypeDB::instance( orig_type );
Ref<Resource> res = Ref<Resource>( inst->cast_to<Resource>() );
@@ -187,6 +192,7 @@ void CustomPropertyEditor::_menu_option(int p_which) {
ERR_FAIL_COND( inheritors_array.empty() );
+
String intype=inheritors_array[p_which-TYPE_BASE_ID];
Object *obj = ObjectTypeDB::instance(intype);
@@ -209,11 +215,11 @@ void CustomPropertyEditor::_menu_option(int p_which) {
}
Variant CustomPropertyEditor::get_variant() const {
-
- return v;
+
+ return v;
}
String CustomPropertyEditor::get_name() const {
-
+
return name;
}
@@ -231,9 +237,11 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
inheritors_array.clear();
text_edit->hide();
easing_draw->hide();
-
+ spinbox->hide();
+ slider->hide();
+
for (int i=0;i<MAX_VALUE_EDITORS;i++) {
-
+
value_editor[i]->hide();
value_label[i]->hide();
if (i<4)
@@ -241,8 +249,8 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
}
for (int i=0;i<MAX_ACTION_BUTTONS;i++) {
-
- action_buttons[i]->hide();
+
+ action_buttons[i]->hide();
}
for(int i=0;i<20;i++)
@@ -252,11 +260,49 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
switch(type) {
-
+
case Variant::INT:
case Variant::REAL: {
- if (hint==PROPERTY_HINT_ALL_FLAGS) {
+ if (hint==PROPERTY_HINT_RANGE) {
+
+ int c = hint_text.get_slice_count(",");
+ float min=0,max=100,step=1;
+ if (c>=1) {
+
+ if (!hint_text.get_slice(",",0).empty())
+ min=hint_text.get_slice(",",0).to_double();
+ }
+ if (c>=2) {
+
+ if (!hint_text.get_slice(",",1).empty())
+ max=hint_text.get_slice(",",1).to_double();
+ }
+
+ if (type==Variant::REAL && c>=3) {
+
+ if (!hint_text.get_slice(",",2).empty())
+ step= hint_text.get_slice(",",2).to_double();
+ }
+
+ if (c>=4 && hint_text.get_slice(",",3)=="slider") {
+ slider->set_min(min);
+ slider->set_max(max);
+ slider->set_step((type==Variant::REAL) ? step : 1);
+ slider->set_val(v);
+ slider->show();
+ set_size(Size2(110,30));
+ } else {
+ spinbox->set_min(min);
+ spinbox->set_max(max);
+ spinbox->set_step((type==Variant::REAL) ? step : 1);
+ spinbox->set_val(v);
+ spinbox->show();
+ set_size(Size2(70,35));
+ }
+
+
+ } else if (hint==PROPERTY_HINT_ALL_FLAGS) {
uint32_t flgs = v;
for(int i=0;i<2;i++) {
@@ -404,7 +450,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
value_editor[3]->set_text( String::num( r.size.y) );
} break;
case Variant::VECTOR3: {
-
+
List<String> names;
names.push_back("x");
names.push_back("y");
@@ -416,7 +462,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
value_editor[2]->set_text( String::num( vec.z) );
} break;
case Variant::PLANE: {
-
+
List<String> names;
names.push_back("x");
names.push_back("y");
@@ -428,10 +474,10 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
value_editor[1]->set_text( String::num( plane.normal.y ) );
value_editor[2]->set_text( String::num( plane.normal.z ) );
value_editor[3]->set_text( String::num( plane.d ) );
-
+
} break;
case Variant::QUAT: {
-
+
List<String> names;
names.push_back("x");
names.push_back("y");
@@ -446,7 +492,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
} break;
case Variant::_AABB: {
-
+
List<String> names;
names.push_back("px");
names.push_back("py");
@@ -455,7 +501,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
names.push_back("sy");
names.push_back("sz");
config_value_editors(6,3,16,names);
-
+
AABB aabb=v;
value_editor[0]->set_text( String::num( aabb.pos.x ) );
value_editor[1]->set_text( String::num( aabb.pos.y ) );
@@ -463,7 +509,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
value_editor[3]->set_text( String::num( aabb.size.x ) );
value_editor[4]->set_text( String::num( aabb.size.y ) );
value_editor[5]->set_text( String::num( aabb.size.z ) );
-
+
} break;
case Variant::MATRIX32: {
@@ -484,7 +530,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
} break;
case Variant::MATRIX3: {
-
+
List<String> names;
names.push_back("xx");
names.push_back("xy");
@@ -496,17 +542,17 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
names.push_back("zy");
names.push_back("zz");
config_value_editors(9,3,16,names);
-
+
Matrix3 basis=v;
for(int i=0;i<9;i++) {
-
+
value_editor[i]->set_text( String::num( basis.elements[i/3][i%3] ) );
}
-
+
} break;
case Variant::TRANSFORM: {
-
-
+
+
List<String> names;
names.push_back("xx");
names.push_back("xy");
@@ -521,20 +567,20 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
names.push_back("zz");
names.push_back("zo");
config_value_editors(12,4,16,names);
-
+
Transform tr=v;
for(int i=0;i<9;i++) {
-
+
value_editor[(i/3)*4+i%3]->set_text( String::num( tr.basis.elements[i/3][i%3] ) );
}
-
+
value_editor[3]->set_text( String::num( tr.origin.x ) );
value_editor[7]->set_text( String::num( tr.origin.y ) );
value_editor[11]->set_text( String::num( tr.origin.z ) );
-
+
} break;
case Variant::COLOR: {
-
+
color_picker->show();
color_picker->set_edit_alpha(hint!=PROPERTY_HINT_COLOR_NO_ALPHA);
@@ -548,13 +594,13 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
float values[4]={c.r,c.g,c.b,c.a};
for (int i=0;i<4;i++) {
int y=m+i*h;
-
+
value_editor[i]->show();
- value_label[i]->show();
+ value_label[i]->show();
value_label[i]->set_pos(Point2(ofs,y));
scroll[i]->set_min(0);
scroll[i]->set_max(1.0);
- scroll[i]->set_page(0);
+ scroll[i]->set_page(0);
scroll[i]->set_pos(Point2(ofs+15,y+Math::floor((h-scroll[i]->get_minimum_size().height)/2.0)));
scroll[i]->set_val(values[i]);
scroll[i]->set_size(Size2(120,1));
@@ -562,30 +608,30 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
value_editor[i]->set_pos(Point2(ofs+140,y));
value_editor[i]->set_size(Size2(40,h));
value_editor[i]->set_text( String::num(values[i],2 ));
-
+
}
-
+
value_label[0]->set_text("R");
value_label[1]->set_text("G");
value_label[2]->set_text("B");
value_label[3]->set_text("A");
-
+
Size2 new_size = value_editor[3]->get_pos() + value_editor[3]->get_size() + Point2(10,10);
set_size( new_size );
*/
-
+
} break;
case Variant::IMAGE: {
-
+
List<String> names;
names.push_back("New");
names.push_back("Load");
names.push_back("Clear");
config_action_buttons(names);
-
+
} break;
case Variant::NODE_PATH: {
-
+
List<String> names;
names.push_back("Assign");
names.push_back("Clear");
@@ -593,7 +639,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
} break;
case Variant::OBJECT: {
-
+
if (hint!=PROPERTY_HINT_RESOURCE_TYPE)
break;
@@ -603,6 +649,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
if (hint_text!="") {
+ int idx=0;
for(int i=0;i<hint_text.get_slice_count(",");i++) {
@@ -612,27 +659,27 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
Set<String> valid_inheritors;
valid_inheritors.insert(base);
- List<String> inheritors;
+ List<StringName> inheritors;
ObjectTypeDB::get_inheriters_from(base.strip_edges(),&inheritors);
- List<String>::Element *E=inheritors.front();
+ List<StringName>::Element *E=inheritors.front();
while(E) {
valid_inheritors.insert(E->get());
E=E->next();
}
- int idx=0;
for(Set<String>::Element *E=valid_inheritors.front();E;E=E->next()) {
String t = E->get();
if (!ObjectTypeDB::can_instance(t))
continue;
inheritors_array.push_back(t);
+ int id = TYPE_BASE_ID+idx;
if (has_icon(t,"EditorIcons")) {
- menu->add_icon_item(get_icon(t,"EditorIcons"),"New "+t,TYPE_BASE_ID+idx);
+ menu->add_icon_item(get_icon(t,"EditorIcons"),"New "+t,id);
} else {
- menu->add_item("New "+t,TYPE_BASE_ID+idx);
+ menu->add_item("New "+t,id);
}
idx++;
@@ -649,6 +696,8 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
if (!RES(v).is_null()) {
+
+
menu->add_icon_item(get_icon("EditResource","EditorIcons"),"Edit",OBJ_MENU_EDIT);
menu->add_icon_item(get_icon("Del","EditorIcons"),"Clear",OBJ_MENU_CLEAR);
menu->add_icon_item(get_icon("Duplicate","EditorIcons"),"Make Unique",OBJ_MENU_MAKE_UNIQUE);
@@ -657,6 +706,13 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
menu->add_separator();
menu->add_icon_item(get_icon("Reload","EditorIcons"),"Re-Import",OBJ_MENU_REIMPORT);
}
+ /*if (r.is_valid() && r->get_path().is_resource_file()) {
+ menu->set_item_tooltip(1,r->get_path());
+ } else if (r.is_valid()) {
+ menu->set_item_tooltip(1,r->get_name()+" ("+r->get_type()+")");
+ }*/
+ } else {
+
}
@@ -689,40 +745,40 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
} break;
case Variant::INPUT_EVENT: {
-
-
+
+
} break;
case Variant::DICTIONARY: {
-
-
+
+
} break;
case Variant::RAW_ARRAY: {
-
-
+
+
} break;
case Variant::INT_ARRAY: {
-
-
+
+
} break;
case Variant::REAL_ARRAY: {
-
-
+
+
} break;
case Variant::STRING_ARRAY: {
-
-
+
+
} break;
case Variant::VECTOR3_ARRAY: {
-
-
+
+
} break;
case Variant::COLOR_ARRAY: {
-
-
+
+
} break;
default: {}
- }
-
+ }
+
updating=false;
return true;
}
@@ -730,7 +786,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty
void CustomPropertyEditor::_file_selected(String p_file) {
switch(type) {
-
+
case Variant::STRING: {
if (hint==PROPERTY_HINT_FILE || hint==PROPERTY_HINT_DIR) {
@@ -755,7 +811,7 @@ void CustomPropertyEditor::_file_selected(String p_file) {
RES res = ResourceLoader::load(p_file,type);
if (res.is_null()) {
error->set_text("Error loading file: Not a resource!");
- error->popup_centered(Size2(300,80));
+ error->popup_centered_minsize();
break;
}
v=res.get_ref_ptr();
@@ -868,9 +924,9 @@ void CustomPropertyEditor::_node_path_selected(NodePath p_path) {
void CustomPropertyEditor::_action_pressed(int p_which) {
- if (updating)
+ if (updating)
return;
-
+
switch(type) {
case Variant::INT: {
@@ -897,11 +953,11 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
if (p_which==0) {
if (hint==PROPERTY_HINT_FILE)
- file->set_access(FileDialog::ACCESS_RESOURCES);
+ file->set_access(EditorFileDialog::ACCESS_RESOURCES);
else
- file->set_access(FileDialog::ACCESS_FILESYSTEM);
+ file->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
- file->set_mode(FileDialog::MODE_OPEN_FILE);
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
file->clear_filters();
file->clear_filters();
@@ -935,10 +991,10 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
if (p_which==0) {
if (hint==PROPERTY_HINT_DIR)
- file->set_access(FileDialog::ACCESS_RESOURCES);
+ file->set_access(EditorFileDialog::ACCESS_RESOURCES);
else
- file->set_access(FileDialog::ACCESS_FILESYSTEM);
- file->set_mode(FileDialog::MODE_OPEN_DIR);
+ file->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
+ file->set_mode(EditorFileDialog::MODE_OPEN_DIR);
file->clear_filters();
file->popup_centered_ratio();
} else {
@@ -953,7 +1009,7 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
} break;
case Variant::NODE_PATH: {
-
+
if (p_which==0) {
@@ -964,58 +1020,60 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
v=NodePath();
emit_signal("variant_changed");
- }
+ }
} break;
case Variant::OBJECT: {
-
+
if (p_which==0) {
-
+
+
ERR_FAIL_COND( inheritors_array.empty() );
String intype=inheritors_array[0];
-
+
+
if (hint==PROPERTY_HINT_RESOURCE_TYPE) {
-
+
Object *obj = ObjectTypeDB::instance(intype);
ERR_BREAK( !obj );
Resource *res=obj->cast_to<Resource>();
ERR_BREAK( !res );
-
+
v=Ref<Resource>(res).get_ref_ptr();
emit_signal("variant_changed");
hide();
-
+
}
} else if (p_which==1) {
-
- file->set_access(FileDialog::ACCESS_RESOURCES);
- file->set_mode(FileDialog::MODE_OPEN_FILE);
+
+ file->set_access(EditorFileDialog::ACCESS_RESOURCES);
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
List<String> extensions;
String type=(hint==PROPERTY_HINT_RESOURCE_TYPE)?hint_text:String();
-
+
ResourceLoader::get_recognized_extensions_for_type(type,&extensions);
file->clear_filters();
for (List<String>::Element *E=extensions.front();E;E=E->next()) {
-
+
file->add_filter("*."+E->get()+" ; "+E->get().to_upper() );
-
+
}
-
+
file->popup_centered_ratio();
-
+
} else if (p_which==2) {
-
+
RefPtr RefPtr=v;
if (!RefPtr.is_null()) {
emit_signal("resource_edit_request");
- hide();
+ hide();
}
-
+
} else if (p_which==3) {
-
-
+
+
v=Variant();
emit_signal("variant_changed");
hide();
@@ -1058,7 +1116,7 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
emit_signal("variant_changed");
hide();
}
-
+
} break;
case Variant::IMAGE: {
@@ -1068,8 +1126,8 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
} else if (p_which==1) {
- file->set_access(FileDialog::ACCESS_RESOURCES);
- file->set_mode(FileDialog::MODE_OPEN_FILE);
+ file->set_access(EditorFileDialog::ACCESS_RESOURCES);
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
List<String> extensions;
ImageLoader::get_recognized_extensions(&extensions);
@@ -1097,16 +1155,16 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
}
void CustomPropertyEditor::_scroll_modified(double p_value) {
-
- if (updating)
+
+ if (updating)
return;
/*
switch(type) {
-
+
case Variant::COLOR: {
-
+
for (int i=0;i<4;i++) {
-
+
value_editor[i]->set_text( String::num(scroll[i]->get_val(),2) );
}
Color c;
@@ -1131,7 +1189,7 @@ void CustomPropertyEditor::_drag_easing(const InputEvent& p_ev) {
float rel = p_ev.mouse_motion.relative_x;
if (rel==0)
- return;
+ return;
bool flip=hint_text=="attenuation";
@@ -1214,7 +1272,7 @@ void CustomPropertyEditor::_text_edit_changed() {
}
void CustomPropertyEditor::_modified(String p_string) {
-
+
if (updating)
return;
updating=true;
@@ -1255,17 +1313,17 @@ void CustomPropertyEditor::_modified(String p_string) {
} break;
case Variant::VECTOR3: {
-
+
Vector3 vec;
vec.x=value_editor[0]->get_text().to_double();
vec.y=value_editor[1]->get_text().to_double();
vec.z=value_editor[2]->get_text().to_double();
v=vec;
emit_signal("variant_changed");
-
+
} break;
case Variant::PLANE: {
-
+
Plane pl;
pl.normal.x=value_editor[0]->get_text().to_double();
pl.normal.y=value_editor[1]->get_text().to_double();
@@ -1273,10 +1331,10 @@ void CustomPropertyEditor::_modified(String p_string) {
pl.d=value_editor[3]->get_text().to_double();
v=pl;
emit_signal("variant_changed");
-
+
} break;
case Variant::QUAT: {
-
+
Quat q;
q.x=value_editor[0]->get_text().to_double();
q.y=value_editor[1]->get_text().to_double();
@@ -1284,10 +1342,10 @@ void CustomPropertyEditor::_modified(String p_string) {
q.w=value_editor[3]->get_text().to_double();
v=q;
emit_signal("variant_changed");
-
+
} break;
case Variant::_AABB: {
-
+
Vector3 pos;
pos.x=value_editor[0]->get_text().to_double();
pos.y=value_editor[1]->get_text().to_double();
@@ -1296,10 +1354,10 @@ void CustomPropertyEditor::_modified(String p_string) {
size.x=value_editor[3]->get_text().to_double();
size.y=value_editor[4]->get_text().to_double();
size.z=value_editor[5]->get_text().to_double();
-
+
v=AABB(pos,size);
emit_signal("variant_changed");
-
+
} break;
case Variant::MATRIX32: {
@@ -1314,39 +1372,39 @@ void CustomPropertyEditor::_modified(String p_string) {
} break;
case Variant::MATRIX3: {
-
+
Matrix3 m;
for(int i=0;i<9;i++) {
-
+
m.elements[i/3][i%3]=value_editor[i]->get_text().to_double();
}
-
+
v=m;
emit_signal("variant_changed");
-
+
} break;
case Variant::TRANSFORM: {
-
+
Matrix3 basis;
for(int i=0;i<9;i++) {
-
+
basis.elements[i/3][i%3]=value_editor[(i/3)*4+i%3]->get_text().to_double();
}
-
+
Vector3 origin;
origin.x=value_editor[3]->get_text().to_double();
origin.y=value_editor[7]->get_text().to_double();
origin.z=value_editor[11]->get_text().to_double();
-
+
v=Transform(basis,origin);
emit_signal("variant_changed");
-
-
+
+
} break;
case Variant::COLOR: {
/*
for (int i=0;i<4;i++) {
-
+
scroll[i]->set_val( value_editor[i]->get_text().to_double() );
}
Color c;
@@ -1360,51 +1418,57 @@ void CustomPropertyEditor::_modified(String p_string) {
*/
} break;
case Variant::IMAGE: {
-
-
+
+
} break;
case Variant::NODE_PATH: {
-
-
- } break;
+
+
+ } break;
case Variant::INPUT_EVENT: {
-
-
+
+
} break;
case Variant::DICTIONARY: {
-
-
+
+
} break;
case Variant::RAW_ARRAY: {
-
-
+
+
} break;
case Variant::INT_ARRAY: {
-
-
+
+
} break;
case Variant::REAL_ARRAY: {
-
-
+
+
} break;
case Variant::STRING_ARRAY: {
-
-
+
+
} break;
case Variant::VECTOR3_ARRAY: {
-
-
+
+
} break;
case Variant::COLOR_ARRAY: {
-
-
+
+
} break;
default: {}
- }
-
+ }
+
updating=false;
}
+void CustomPropertyEditor::_range_modified(double p_value)
+{
+ v=p_value;
+ emit_signal("variant_changed");
+}
+
void CustomPropertyEditor::_focus_enter() {
switch(type) {
case Variant::REAL:
@@ -1461,10 +1525,10 @@ void CustomPropertyEditor::config_action_buttons(const List<String>& p_strings)
set_size( Size2( w, m*2+(h+m)*p_strings.size() ) );
for (int i=0;i<MAX_ACTION_BUTTONS;i++) {
-
+
if (i<p_strings.size()) {
action_buttons[i]->show();
- action_buttons[i]->set_text(p_strings[i]);
+ action_buttons[i]->set_text(p_strings[i]);
action_buttons[i]->set_pos( Point2( m, m+i*(h+m) ));
action_buttons[i]->set_size( Size2( w-m*2, h ) );
action_buttons[i]->set_flat(true);
@@ -1472,29 +1536,29 @@ void CustomPropertyEditor::config_action_buttons(const List<String>& p_strings)
action_buttons[i]->hide();
}
}
-
-
+
+
}
void CustomPropertyEditor::config_value_editors(int p_amount, int p_columns,int p_label_w,const List<String>& p_strings) {
-
+
int w=80;
int h=20;
int m=10;
-
+
int rows=((p_amount-1)/p_columns)+1;
-
+
set_size( Size2( m*(1+p_columns)+(w+p_label_w)*p_columns, m*(1+rows)+h*rows ) );
-
+
for (int i=0;i<MAX_VALUE_EDITORS;i++) {
-
+
int c=i%p_columns;
int r=i/p_columns;
-
+
if (i<p_amount) {
value_editor[i]->show();
value_label[i]->show();
- value_label[i]->set_text(i<p_strings.size()?p_strings[i]:String(""));
+ value_label[i]->set_text(i<p_strings.size()?p_strings[i]:String(""));
value_editor[i]->set_pos( Point2( m+p_label_w+c*(w+m+p_label_w), m+r*(h+m) ));
value_editor[i]->set_size( Size2( w, h ) );
value_label[i]->set_pos( Point2( m+c*(w+m+p_label_w), m+r*(h+m) ) );
@@ -1504,16 +1568,17 @@ void CustomPropertyEditor::config_value_editors(int p_amount, int p_columns,int
value_label[i]->hide();
}
}
-
-
-
+
+
+
}
void CustomPropertyEditor::_bind_methods() {
-
+
ObjectTypeDB::bind_method("_focus_enter", &CustomPropertyEditor::_focus_enter);
ObjectTypeDB::bind_method("_focus_exit", &CustomPropertyEditor::_focus_exit);
ObjectTypeDB::bind_method("_modified",&CustomPropertyEditor::_modified);
+ ObjectTypeDB::bind_method("_range_modified", &CustomPropertyEditor::_range_modified);
ObjectTypeDB::bind_method("_scroll_modified",&CustomPropertyEditor::_scroll_modified);
ObjectTypeDB::bind_method("_action_pressed",&CustomPropertyEditor::_action_pressed);
ObjectTypeDB::bind_method("_file_selected",&CustomPropertyEditor::_file_selected);
@@ -1530,13 +1595,13 @@ void CustomPropertyEditor::_bind_methods() {
ADD_SIGNAL( MethodInfo("resource_edit_request") );
}
CustomPropertyEditor::CustomPropertyEditor() {
-
-
+
+
read_only=false;
updating=false;
-
+
for (int i=0;i<MAX_VALUE_EDITORS;i++) {
-
+
value_editor[i]=memnew( LineEdit );
add_child( value_editor[i] );
value_label[i]=memnew( Label );
@@ -1547,17 +1612,17 @@ CustomPropertyEditor::CustomPropertyEditor() {
value_editor[i]->connect("focus_enter", this, "_focus_enter");
value_editor[i]->connect("focus_exit", this, "_focus_exit");
}
-
+
for(int i=0;i<4;i++) {
-
+
scroll[i] = memnew( HScrollBar );
scroll[i]->hide();
scroll[i]->set_min(0);
scroll[i]->set_max(1.0);
- scroll[i]->set_step(0.01);
+ scroll[i]->set_step(0.01);
add_child(scroll[i]);
scroll[i]->connect("value_changed", this,"_scroll_modified");
-
+
}
for(int i=0;i<20;i++) {
@@ -1581,15 +1646,15 @@ CustomPropertyEditor::CustomPropertyEditor() {
text_edit->connect("text_changed",this,"_text_edit_changed");
for (int i=0;i<MAX_ACTION_BUTTONS;i++) {
-
+
action_buttons[i]=memnew(Button);
- action_buttons[i]->hide();
+ action_buttons[i]->hide();
add_child(action_buttons[i]);
Vector<Variant> binds;
binds.push_back(i);
action_buttons[i]->connect("pressed", this,"_action_pressed",binds);
}
-
+
color_picker = memnew( ColorPicker );
add_child(color_picker);
color_picker->hide();
@@ -1599,10 +1664,10 @@ CustomPropertyEditor::CustomPropertyEditor() {
color_picker->connect("color_changed",this,"_color_changed");
set_as_toplevel(true);
- file = memnew ( FileDialog );
+ file = memnew ( EditorFileDialog );
add_child(file);
file->hide();
-
+
file->connect("file_selected", this,"_file_selected");
file->connect("dir_selected", this,"_file_selected");
@@ -1610,17 +1675,17 @@ CustomPropertyEditor::CustomPropertyEditor() {
error->set_title("Error!");
add_child(error);
//error->get_cancel()->hide();
-
+
type_button = memnew( MenuButton );
add_child(type_button);
type_button->hide();
type_button->get_popup()->connect("item_pressed", this,"_type_create_selected");
-
+
scene_tree = memnew( SceneTreeDialog );
add_child(scene_tree);
scene_tree->connect("selected", this,"_node_path_selected");
- scene_tree->get_tree()->set_show_enabled_subscene(true);
+ scene_tree->get_scene_tree()->set_show_enabled_subscene(true);
texture_preview = memnew( TextureFrame );
add_child( texture_preview);
@@ -1637,52 +1702,182 @@ CustomPropertyEditor::CustomPropertyEditor() {
menu = memnew(PopupMenu);
add_child(menu);
menu->connect("item_pressed",this,"_menu_option");
-}
+ spinbox = memnew ( SpinBox );
+ add_child(spinbox);
+ spinbox->set_area_as_parent_rect(5);
+ spinbox->connect("value_changed",this,"_range_modified");
+ slider = memnew ( HSlider );
+ add_child(slider);
+ slider->set_area_as_parent_rect(5);
+ slider->connect("value_changed",this,"_range_modified");
+}
-Node *PropertyEditor::get_instanced_node() {
+bool PropertyEditor::_might_be_in_instance() {
- //this sucks badly
if (!obj)
return NULL;
Node *node = obj->cast_to<Node>();
+
+ Node* edited_scene =EditorNode::get_singleton()->get_edited_scene();
+
+ bool might_be=false;
+
+ while(node) {
+
+ if (node->get_scene_instance_state().is_valid()) {
+ might_be=true;
+ break;
+ }
+ if (node==edited_scene) {
+ if (node->get_scene_inherited_state().is_valid()) {
+ might_be=true;
+ break;
+ }
+ might_be=false;
+ break;
+ }
+ node=node->get_owner();
+ }
+
+ return might_be;
+
+}
+
+bool PropertyEditor::_get_instanced_node_original_property(const StringName& p_prop, Variant& value) {
+
+ Node *node = obj->cast_to<Node>();
+
if (!node)
- return NULL;
+ return false;
- if (node->get_filename()=="")
- return NULL;
+ Node *orig=node;
+
+ Node* edited_scene =EditorNode::get_singleton()->get_edited_scene();
+
+ bool found=false;
+
+// print_line("for prop - "+String(p_prop));
- if (!node->get_owner())
- return NULL; //scene root i guess
- return node;
+ while(node) {
+
+ Ref<SceneState> ss;
+
+ if (node==edited_scene) {
+ ss=node->get_scene_inherited_state();
+
+ } else {
+ ss=node->get_scene_instance_state();
+ }
+ // print_line("at - "+String(edited_scene->get_path_to(node)));
+
+ if (ss.is_valid()) {
+
+ NodePath np = node->get_path_to(orig);
+ int node_idx = ss->find_node_by_path(np);
+ // print_line("\t valid, nodeidx "+itos(node_idx));
+ if (node_idx>=0) {
+ bool lfound=false;
+ Variant lvar;
+ lvar=ss->get_property_value(node_idx,p_prop,lfound);
+ if (lfound) {
+
+ found=true;
+ value=lvar;
+ // print_line("\t found value "+String(value));
+ }
+ }
+ }
+ if (node==edited_scene) {
+ //just in case
+ break;
+ }
+ node=node->get_owner();
+
+ }
+
+ return found;
+}
+
+bool PropertyEditor::_is_property_different(const Variant& p_current, const Variant& p_orig,int p_usage) {
+
+
+ {
+ Node *node = obj->cast_to<Node>();
+ if (!node)
+ return false;
+
+ Node* edited_scene =EditorNode::get_singleton()->get_edited_scene();
+ bool found_state=false;
+
+ // print_line("for prop - "+String(p_prop));
+
+
+ while(node) {
+
+ Ref<SceneState> ss;
+
+ if (node==edited_scene) {
+ ss=node->get_scene_inherited_state();
+
+ } else {
+ ss=node->get_scene_instance_state();
+ }
+
+ if (ss.is_valid()) {
+ found_state=true;
+ }
+ if (node==edited_scene) {
+ //just in case
+ break;
+ }
+ node=node->get_owner();
+ }
+
+ if (!found_state)
+ return false; //pointless to check if we are not comparing against anything.
+ }
+
+ if (p_orig.get_type()==Variant::NIL) {
+
+
+
+ //special cases
+ if (p_current.is_zero() && p_usage&PROPERTY_USAGE_STORE_IF_NONZERO)
+ return false;
+ if (p_current.is_one() && p_usage&PROPERTY_USAGE_STORE_IF_NONONE)
+ return false;
+ }
+
+ return bool(Variant::evaluate(Variant::OP_NOT_EQUAL,p_current,p_orig));
}
TreeItem *PropertyEditor::find_item(TreeItem *p_item,const String& p_name) {
-
-
+
+
if (!p_item)
return NULL;
-
+
String name = p_item->get_metadata(1);
-
+
if (name==p_name) {
-
+
return p_item;
}
-
+
TreeItem *c=p_item->get_children();
-
+
while (c) {
-
+
TreeItem *found = find_item(c,p_name);
if (found)
return found;
c=c->get_next();
}
-
+
return NULL;
}
@@ -1705,9 +1900,9 @@ void PropertyEditor::_changed_callbacks(Object *p_changed,const String& p_prop)
if (p_prop==String())
update_tree_pending=true;
else {
-
+
pending[p_prop]=p_prop;
-
+
}
}
@@ -1719,7 +1914,7 @@ void PropertyEditor::update_property(const String& p_prop) {
void PropertyEditor::set_item_text(TreeItem *p_item, int p_type, const String& p_name, int p_hint, const String& p_hint_text) {
-
+
switch( p_type ) {
case Variant::BOOL: {
@@ -1812,7 +2007,7 @@ void PropertyEditor::set_item_text(TreeItem *p_item, int p_type, const String& p
} break;
case Variant::IMAGE: {
-
+
Image img = obj->get( p_name );
if (img.empty())
p_item->set_text(1,"[Image (empty)]");
@@ -1853,14 +2048,49 @@ void PropertyEditor::set_item_text(TreeItem *p_item, int p_type, const String& p
} else {
p_item->set_text(1,"<"+res->get_type()+">");
};
+
+
+ if (res.is_valid() && res->get_path().is_resource_file()) {
+ p_item->set_tooltip(1,res->get_path());
+ } else if (res.is_valid()) {
+ p_item->set_tooltip(1,res->get_name()+" ("+res->get_type()+")");
+ }
+
+
+ if (has_icon(res->get_type(),"EditorIcons")) {
+
+ p_item->set_icon(0,get_icon(res->get_type(),"EditorIcons"));
+ } else {
+
+ Dictionary d = p_item->get_metadata(0);
+ int hint=d.has("hint")?d["hint"].operator int():-1;
+ String hint_text=d.has("hint_text")?d["hint_text"]:"";
+ if (hint==PROPERTY_HINT_RESOURCE_TYPE) {
+
+ if (has_icon(hint_text,"EditorIcons")) {
+
+ p_item->set_icon(0,get_icon(hint_text,"EditorIcons"));
+
+ } else {
+ p_item->set_icon(0,get_icon("Object","EditorIcons"));
+
+ }
+ }
+ }
+
+
+
}
+
} break;
default: {};
}
-
+
}
+
+
void PropertyEditor::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_TREE) {
@@ -1869,35 +2099,48 @@ void PropertyEditor::_notification(int p_what) {
}
if (p_what==NOTIFICATION_EXIT_TREE) {
+ get_tree()->disconnect("node_removed",this,"_node_removed");
edit(NULL);
}
-
+
if (p_what==NOTIFICATION_FIXED_PROCESS) {
-
-
+
+
+ if (refresh_countdown>0) {
+ refresh_countdown-=get_fixed_process_delta_time();
+ if (refresh_countdown<=0) {
+ TreeItem *root = tree->get_root();
+ _refresh_item(root);
+ }
+ }
+
changing=true;
-
+
if (update_tree_pending) {
update_tree();
update_tree_pending=false;
-
+
} else {
-
- const String *k=NULL;
+
+ const String *k=NULL;
while ((k=pending.next(k))) {
-
+
TreeItem * item = find_item(tree->get_root(),*k);
if (!item)
continue;
-
- if (get_instanced_node()) {
- Dictionary d = get_instanced_node()->get_instance_state();
- if (d.has(*k)) {
+ if (_might_be_in_instance()) {
+
+
+ Variant vorig;
+ Dictionary d=item->get_metadata(0);
+ int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0;
+
+
+ if (_get_instanced_node_original_property(*k,vorig) || usage) {
Variant v = obj->get(*k);
- Variant vorig = d[*k];
int found=-1;
for(int i=0;i<item->get_button_count(1);i++) {
@@ -1908,7 +2151,7 @@ void PropertyEditor::_notification(int p_what) {
}
}
- bool changed = ! (v==vorig);
+ bool changed = _is_property_different(v,vorig,usage);
if ((found!=-1)!=changed) {
@@ -1925,15 +2168,15 @@ void PropertyEditor::_notification(int p_what) {
}
}
- Dictionary d=item->get_metadata(0);
+ Dictionary d=item->get_metadata(0);
set_item_text(item,d["type"],d["name"],d["hint"],d["hint_text"]);
- }
+ }
}
-
+
pending.clear();
-
+
changing=false;
-
+
}
}
@@ -1982,7 +2225,73 @@ TreeItem *PropertyEditor::get_parent_node(String p_path,HashMap<String,TreeItem*
}
+void PropertyEditor::_refresh_item(TreeItem *p_item) {
+
+ if (!p_item)
+ return;
+
+ String name = p_item->get_metadata(1);
+
+ if (name!=String()) {
+
+ if (_might_be_in_instance()) {
+
+ Variant vorig;
+ Dictionary d=p_item->get_metadata(0);
+ int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0;
+
+ if (_get_instanced_node_original_property(name,vorig) || usage) {
+ Variant v = obj->get(name);
+
+ int found=-1;
+ for(int i=0;i<p_item->get_button_count(1);i++) {
+
+ if (p_item->get_button_id(1,i)==3) {
+ found=i;
+ break;
+ }
+ }
+
+ bool changed = _is_property_different(v,vorig,usage);
+
+ if ((found!=-1)!=changed) {
+
+ if (changed) {
+
+ p_item->add_button(1,get_icon("Reload","EditorIcons"),3);
+ } else {
+
+ p_item->erase_button(1,found);
+ }
+
+ }
+
+ }
+
+ }
+
+ Dictionary d=p_item->get_metadata(0);
+ set_item_text(p_item,d["type"],d["name"],d["hint"],d["hint_text"]);
+ }
+
+ TreeItem *c=p_item->get_children();
+
+ while (c) {
+
+ _refresh_item(c);
+
+ c=c->get_next();
+ }
+
+}
+
+void PropertyEditor::refresh() {
+
+ if (refresh_countdown>0)
+ return;
+ refresh_countdown=EditorSettings::get_singleton()->get("property_editor/auto_refresh_interval");
+}
void PropertyEditor::update_tree() {
@@ -2054,6 +2363,8 @@ void PropertyEditor::update_tree() {
TreeItem * current_category=NULL;
+ String filter = search_box ? search_box->get_text() : "";
+
for (List<PropertyInfo>::Element *I=plist.front() ; I ; I=I->next()) {
PropertyInfo& p = I->get();
@@ -2093,6 +2404,23 @@ void PropertyEditor::update_tree() {
sep->set_selectable(1,false);
sep->set_custom_bg_color(0,get_color("prop_category","Editor"));
sep->set_custom_bg_color(1,get_color("prop_category","Editor"));
+
+ if (use_doc_hints) {
+ StringName type=p.name;
+ if (!class_descr_cache.has(type)) {
+
+ String descr;
+ DocData *dd=EditorHelp::get_doc_data();
+ Map<String,DocData::ClassDoc>::Element *E=dd->class_list.find(type);
+ if (E) {
+ descr=E->get().brief_description;
+ }
+ class_descr_cache[type]=descr.world_wrap(80);
+
+ }
+
+ sep->set_tooltip(0,"Class: "+p.name+":\n\n"+class_descr_cache[type]);
+ }
//sep->set_custom_color(0,Color(1,1,1));
@@ -2100,7 +2428,24 @@ void PropertyEditor::update_tree() {
} else if ( ! (p.usage&PROPERTY_USAGE_EDITOR ) )
continue;
+ String name = (p.name.find("/")!=-1)?p.name.right( p.name.find_last("/")+1 ):p.name;
+
+ if (capitalize_paths)
+ name = name.camelcase_to_underscore().capitalize();
+
String path=p.name.left( p.name.find_last("/") ) ;
+
+ if (use_filter && filter!="") {
+
+ String cat = path;
+
+ if (capitalize_paths)
+ cat = cat.capitalize();
+
+ if (cat.findn(filter)==-1 && name.findn(filter)==-1)
+ continue;
+ }
+
//printf("property %s\n",p.name.ascii().get_data());
TreeItem * parent = get_parent_node(path,item_path,current_category?current_category:root );
//if (parent->get_parent()==root)
@@ -2122,8 +2467,6 @@ void PropertyEditor::update_tree() {
TreeItem * item = tree->create_item( parent );
- String name = (p.name.find("/")!=-1)?p.name.right( p.name.find_last("/")+1 ):p.name;
-
if (level>0) {
item->set_custom_bg_color(0,col);
//item->set_custom_bg_color(1,col);
@@ -2139,32 +2482,65 @@ void PropertyEditor::update_tree() {
item->set_checked(0,p.usage&PROPERTY_USAGE_CHECKED);
}
- if (capitalize_paths)
- item->set_text( 0, name.capitalize() );
- else
- item->set_text( 0, name );
-
+ item->set_text(0, name);
item->set_tooltip(0, p.name);
+ if (use_doc_hints) {
+ StringName setter;
+ StringName type;
+ if (ObjectTypeDB::get_setter_and_type_for_property(obj->get_type_name(),p.name,type,setter)) {
+
+ String descr;
+ bool found=false;
+ Map<StringName,Map<StringName,String> >::Element *E=descr_cache.find(type);
+ if (E) {
+
+ Map<StringName,String>::Element *F=E->get().find(setter);
+ if (F) {
+ found=true;
+ descr=F->get();
+ }
+ }
+ if (!found) {
+
+ DocData *dd=EditorHelp::get_doc_data();
+ Map<String,DocData::ClassDoc>::Element *E=dd->class_list.find(type);
+ if (E) {
+ for(int i=0;i<E->get().methods.size();i++) {
+ if (E->get().methods[i].name==setter.operator String()) {
+ descr=E->get().methods[i].description.strip_edges().world_wrap(80);
+ }
+ }
+ }
+
+ descr_cache[type][setter]=descr;
+ }
+
+ item->set_tooltip(0, "Property: "+p.name+"\n\n"+descr);
+ }
+ }
+ //EditorHelp::get_doc_data();
+
Dictionary d;
d["name"]=p.name;
d["type"]=(int)p.type;
d["hint"]=(int)p.hint;
d["hint_text"]=p.hint_string;
-
+ d["usage"]=(int)p.usage;
+
item->set_metadata( 0, d );
item->set_metadata( 1, p.name );
if (draw_red)
item->set_custom_color(0,Color(0.8,0.4,0.20));
-
+
if (p.name==selected_property) {
item->select(1);
}
-
+
//printf("property %s type %i\n",p.name.ascii().get_data(),p.type);
switch( p.type ) {
@@ -2228,8 +2604,10 @@ void PropertyEditor::update_tree() {
item->set_cell_mode( 1, TreeItem::CELL_MODE_RANGE );
+ if (p.hint==PROPERTY_HINT_SPRITE_FRAME) {
+ item->set_range_config(1,0,99999,1);
- if (p.hint==PROPERTY_HINT_RANGE || p.hint==PROPERTY_HINT_EXP_RANGE) {
+ } else if (p.hint==PROPERTY_HINT_RANGE || p.hint==PROPERTY_HINT_EXP_RANGE) {
int c = p.hint_string.get_slice_count(",");
float min=0,max=100,step=1;
@@ -2245,8 +2623,8 @@ void PropertyEditor::update_tree() {
if (p.type==Variant::REAL && c>=3) {
step= p.hint_string.get_slice(",",2).to_double();
- }
-
+ }
+
item->set_range_config(1,min,max,step,p.hint==PROPERTY_HINT_EXP_RANGE);
} else if (p.hint==PROPERTY_HINT_ENUM) {
@@ -2261,7 +2639,7 @@ void PropertyEditor::update_tree() {
} else {
if (p.type == Variant::REAL) {
- item->set_range_config(1, -65536, 65535, 0.01);
+ item->set_range_config(1, -65536, 65535, 0.001);
} else {
item->set_range_config(1, -65536, 65535, 1);
@@ -2330,11 +2708,32 @@ void PropertyEditor::update_tree() {
}
} break;
+ case Variant::ARRAY: {
+
+ item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM );
+ item->add_button(1,get_icon("EditResource","EditorIcons"));
+
+
+ Variant v = obj->get(p.name);
+ if (v.is_array())
+ item->set_text(1,"Array["+itos(v.call("size"))+"]");
+ else
+ item->set_text(1,"Array[]");
+ item->set_icon( 0, get_icon("ArrayData","EditorIcons") );
+
+
+ } break;
+
case Variant::INT_ARRAY: {
item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM );
- item->set_editable( 1, !read_only );
- item->set_text(1,"[IntArray]");
+ item->add_button(1,get_icon("EditResource","EditorIcons"));
+
+ Variant v = obj->get(p.name);
+ if (v.is_array())
+ item->set_text(1,"IntArray["+itos(v.call("size"))+"]");
+ else
+ item->set_text(1,"IntArray[]");
item->set_icon( 0, get_icon("ArrayInt","EditorIcons") );
@@ -2342,26 +2741,86 @@ void PropertyEditor::update_tree() {
case Variant::REAL_ARRAY: {
item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM );
- item->set_editable( 1, !read_only );
- item->set_text(1,"[RealArray]");
+ item->add_button(1,get_icon("EditResource","EditorIcons"));
+
+ Variant v = obj->get(p.name);
+ if (v.is_array())
+ item->set_text(1,"FloatArray["+itos(v.call("size"))+"]");
+ else
+ item->set_text(1,"FloatArray[]");
item->set_icon( 0, get_icon("ArrayReal","EditorIcons") );
+
} break;
case Variant::STRING_ARRAY: {
item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM );
- item->set_editable( 1, !read_only );
- item->set_text(1,"[StringArray]");
+ item->add_button(1,get_icon("EditResource","EditorIcons"));
+
+ Variant v = obj->get(p.name);
+ if (v.is_array())
+ item->set_text(1,"String["+itos(v.call("size"))+"]");
+ else
+ item->set_text(1,"String[]");
item->set_icon( 0, get_icon("ArrayString","EditorIcons") );
+
} break;
case Variant::RAW_ARRAY: {
item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM );
- item->set_editable( 1, !read_only );
- item->set_text(1,"[Raw Data]");
+ item->add_button(1,get_icon("EditResource","EditorIcons"));
+
+ Variant v = obj->get(p.name);
+ if (v.is_array())
+ item->set_text(1,"Byte["+itos(v.call("size"))+"]");
+ else
+ item->set_text(1,"Byte[]");
item->set_icon( 0, get_icon("ArrayData","EditorIcons") );
+
+ } break;
+ case Variant::VECTOR2_ARRAY: {
+
+ item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM );
+ item->add_button(1,get_icon("EditResource","EditorIcons"));
+
+ Variant v = obj->get(p.name);
+ if (v.is_array())
+ item->set_text(1,"Vector2["+itos(v.call("size"))+"]");
+ else
+ item->set_text(1,"Vector2[]");
+ item->set_icon( 0, get_icon("Vector2","EditorIcons") );
+
+
+ } break;
+ case Variant::VECTOR3_ARRAY: {
+
+ item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM );
+ item->add_button(1,get_icon("EditResource","EditorIcons"));
+
+ Variant v = obj->get(p.name);
+ if (v.is_array())
+ item->set_text(1,"Vector3["+itos(v.call("size"))+"]");
+ else
+ item->set_text(1,"Vector3[]");
+ item->set_icon( 0, get_icon("Vector","EditorIcons") );
+
+
+ } break;
+ case Variant::COLOR_ARRAY: {
+
+ item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM );
+ item->add_button(1,get_icon("EditResource","EditorIcons"));
+
+ Variant v = obj->get(p.name);
+ if (v.is_array())
+ item->set_text(1,"Color["+itos(v.call("size"))+"]");
+ else
+ item->set_text(1,"Color[]");
+ item->set_icon( 0, get_icon("Color","EditorIcons") );
+
+
} break;
case Variant::VECTOR2: {
@@ -2429,7 +2888,7 @@ void PropertyEditor::update_tree() {
} break;
case Variant::IMAGE: {
-
+
item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM );
item->set_editable( 1, !read_only );
Image img = obj->get( p.name );
@@ -2438,7 +2897,7 @@ void PropertyEditor::update_tree() {
else
item->set_text(1,"[Image "+itos(img.get_width())+"x"+itos(img.get_height())+"]");
item->set_icon( 0,get_icon("Image","EditorIcons") );
-
+
} break;
case Variant::NODE_PATH: {
@@ -2453,7 +2912,10 @@ void PropertyEditor::update_tree() {
item->set_editable( 1, !read_only );
item->add_button(1,get_icon("EditResource","EditorIcons"));
String type;
+ if (p.hint==PROPERTY_HINT_RESOURCE_TYPE)
+ type=p.hint_string;
bool notnil=false;
+
if (obj->get( p.name ).get_type() == Variant::NIL || obj->get( p.name ).operator RefPtr().is_null()) {
item->set_text(1,"<null>");
@@ -2477,12 +2939,25 @@ void PropertyEditor::update_tree() {
};
notnil=true;
+ if (has_icon(res->get_type(),"EditorIcons")) {
+ type=res->get_type();
+ }
+
+ if (res.is_valid() && res->get_path().is_resource_file()) {
+ item->set_tooltip(1,res->get_path());
+ } else if (res.is_valid()) {
+ item->set_tooltip(1,res->get_name()+" ("+res->get_type()+")");
+ }
+
}
- if (p.hint==PROPERTY_HINT_RESOURCE_TYPE) {
+
+ if (type!=String()) {
+ if (type.find(",")!=-1)
+ type=type.get_slice(",",0);
//printf("prop %s , type %s\n",p.name.ascii().get_data(),p.hint_string.ascii().get_data());
- if (has_icon(p.hint_string,"EditorIcons"))
- item->set_icon( 0, get_icon(p.hint_string,"EditorIcons") );
+ if (has_icon(type,"EditorIcons"))
+ item->set_icon( 0, get_icon(type,"EditorIcons") );
else
item->set_icon( 0, get_icon("Object","EditorIcons") );
}
@@ -2495,17 +2970,25 @@ void PropertyEditor::update_tree() {
if (keying) {
- item->add_button(1,get_icon("Key","EditorIcons"),2);
+ if (p.hint==PROPERTY_HINT_SPRITE_FRAME) {
+
+ item->add_button(1,get_icon("KeyNext","EditorIcons"),5);
+ } else {
+ item->add_button(1,get_icon("Key","EditorIcons"),2);
+ }
}
- if (get_instanced_node()) {
+ if (_might_be_in_instance()) {
- Dictionary d = get_instanced_node()->get_instance_state();
- if (d.has(p.name)) {
+ Variant vorig;
+ Dictionary d=item->get_metadata(0);
+ int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0;
+ if (_get_instanced_node_original_property(p.name,vorig) || usage) {
Variant v = obj->get(p.name);
- Variant vorig = d[p.name];
- if (! (v==vorig)) {
+
+ if (_is_property_different(v,vorig,usage)) {
+ //print_line("FOR "+String(p.name)+" RELOAD WITH: "+String(v)+"("+Variant::get_type_name(v.get_type())+")=="+String(vorig)+"("+Variant::get_type_name(vorig.get_type())+")");
item->add_button(1,get_icon("Reload","EditorIcons"),3);
}
}
@@ -2536,7 +3019,7 @@ void PropertyEditor::_edit_set(const String& p_name, const Variant& p_value) {
}
}
- if (!undo_redo) {
+ if (!undo_redo || obj->cast_to<MultiNodeEdit>() || obj->cast_to<ArrayPropertyEdit>()) { //kind of hacky
obj->set(p_name,p_value);
_changed_callbacks(obj,p_name);
@@ -2568,10 +3051,10 @@ void PropertyEditor::_edit_set(const String& p_name, const Variant& p_value) {
void PropertyEditor::_item_edited() {
-
- TreeItem * item = tree->get_edited();
+
+ TreeItem * item = tree->get_edited();
Dictionary d = item->get_metadata(0);
-
+
String name=d["name"];
if (tree->get_edited_column()==0) {
@@ -2600,12 +3083,12 @@ void PropertyEditor::_item_edited() {
int hint= d["hint"];
String hint_text=d["hint_text"];
switch(type) {
-
+
case Variant::NIL: {
-
- } break;
+
+ } break;
case Variant::BOOL: {
-
+
_edit_set(name,item->is_checked(1));
} break;
case Variant::INT:
@@ -2624,7 +3107,7 @@ void PropertyEditor::_item_edited() {
_edit_set(name,item->get_range(1));
} break;
case Variant::STRING: {
-
+
if (hint==PROPERTY_HINT_ENUM) {
int idx= item->get_range(1);
@@ -2642,64 +3125,64 @@ void PropertyEditor::_item_edited() {
}
} break;
// math types
-
+
case Variant::VECTOR3: {
-
+
} break;
case Variant::PLANE: {
-
+
} break;
case Variant::QUAT: {
-
+
} break;
case Variant::_AABB: {
-
+
} break;
case Variant::MATRIX3: {
-
+
} break;
case Variant::TRANSFORM: {
-
+
} break;
-
+
case Variant::COLOR: {
//_edit_set(name,item->get_custom_bg_color(0));
} break;
case Variant::IMAGE: {
-
+
} break;
case Variant::NODE_PATH: {
-
+
} break;
case Variant::INPUT_EVENT: {
-
+
} break;
case Variant::DICTIONARY: {
-
+
} break;
-
+
// arrays
case Variant::RAW_ARRAY: {
-
+
} break;
case Variant::INT_ARRAY: {
-
+
} break;
case Variant::REAL_ARRAY: {
-
+
} break;
case Variant::STRING_ARRAY: {
-
+
} break;
case Variant::VECTOR3_ARRAY: {
-
+
} break;
case Variant::COLOR_ARRAY: {
-
+
} break;
-
-
+
+
};
}
@@ -2750,24 +3233,34 @@ void PropertyEditor::_custom_editor_request(bool p_arrow) {
}
void PropertyEditor::edit(Object* p_object) {
-
+
if (obj==p_object)
return;
if (obj) {
-
+
obj->remove_change_receptor(this);
}
obj=p_object;
update_tree();
-
+
if (obj) {
-
+
obj->add_change_receptor(this);
}
-
-
+
+
+}
+
+void PropertyEditor::_set_range_def(Object *p_item, String prop,float p_frame) {
+
+ TreeItem *ti = p_item->cast_to<TreeItem>();
+ ERR_FAIL_COND(!ti);
+
+ ti->call_deferred("set_range",1, p_frame);
+ obj->call_deferred("set",prop,p_frame);
+
}
void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) {
@@ -2781,20 +3274,29 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) {
if (!d.has("name"))
return;
String prop=d["name"];
- emit_signal("property_keyed",prop,obj->get(prop));
+ emit_signal("property_keyed",prop,obj->get(prop),false);
+ } else if (p_button==5) {
+ print_line("PB5");
+ if (!d.has("name"))
+ return;
+ String prop=d["name"];
+ emit_signal("property_keyed",prop,obj->get(prop),true);
+ //set_range(p_column, ti->get_range(p_column)+1.0 );
+ call_deferred("_set_range_def",ti,prop,ti->get_range(p_column)+1.0);
} else if (p_button==3) {
- if (!get_instanced_node())
+ if (!_might_be_in_instance())
return;
if (!d.has("name"))
return;
String prop=d["name"];
- Dictionary d2 = get_instanced_node()->get_instance_state();
- if (d2.has(prop)) {
+ Variant vorig;
+
+ if (_get_instanced_node_original_property(prop,vorig)) {
- _edit_set(prop,d2[prop]);
+ _edit_set(prop,vorig);
}
} else {
@@ -2838,15 +3340,28 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) {
emit_signal("resource_selected",r,n);
}
+ } else if (t==Variant::ARRAY || t==Variant::INT_ARRAY || t==Variant::REAL_ARRAY || t==Variant::STRING_ARRAY || t==Variant::VECTOR2_ARRAY || t==Variant::VECTOR3_ARRAY || t==Variant::COLOR_ARRAY || t==Variant::RAW_ARRAY) {
+
+ Variant v = obj->get(n);
+
+ if (v.get_type()!=t) {
+ Variant::CallError ce;
+ v=Variant::construct(Variant::Type(t),NULL,0,ce);
+ }
+
+ Ref<ArrayPropertyEdit> ape = memnew( ArrayPropertyEdit );
+ ape->edit(obj,n,Variant::Type(t));
+
+ EditorNode::get_singleton()->push_item(ape.ptr());
}
}
}
void PropertyEditor::_node_removed(Node *p_node) {
-
+
if (p_node==obj) {
-
+
edit(NULL);
}
}
@@ -2901,17 +3416,24 @@ void PropertyEditor::_draw_flags(Object *t,const Rect2& p_rect) {
}
+void PropertyEditor::_filter_changed(const String& p_text) {
+
+ update_tree();
+}
+
void PropertyEditor::_bind_methods() {
ObjectTypeDB::bind_method( "_item_edited",&PropertyEditor::_item_edited);
ObjectTypeDB::bind_method( "_item_selected",&PropertyEditor::_item_selected);
ObjectTypeDB::bind_method( "_custom_editor_request",&PropertyEditor::_custom_editor_request);
- ObjectTypeDB::bind_method( "_custom_editor_edited",&PropertyEditor::_custom_editor_edited);
- ObjectTypeDB::bind_method( "_resource_edit_request",&PropertyEditor::_resource_edit_request);
- ObjectTypeDB::bind_method( "_node_removed",&PropertyEditor::_node_removed);
+ ObjectTypeDB::bind_method( "_custom_editor_edited",&PropertyEditor::_custom_editor_edited);
+ ObjectTypeDB::bind_method( "_resource_edit_request",&PropertyEditor::_resource_edit_request);
+ ObjectTypeDB::bind_method( "_node_removed",&PropertyEditor::_node_removed);
ObjectTypeDB::bind_method( "_edit_button",&PropertyEditor::_edit_button);
ObjectTypeDB::bind_method( "_changed_callback",&PropertyEditor::_changed_callbacks);
ObjectTypeDB::bind_method( "_draw_flags",&PropertyEditor::_draw_flags);
+ ObjectTypeDB::bind_method( "_set_range_def",&PropertyEditor::_set_range_def);
+ ObjectTypeDB::bind_method( "_filter_changed",&PropertyEditor::_filter_changed);
ADD_SIGNAL( MethodInfo("property_toggled",PropertyInfo( Variant::STRING, "property"),PropertyInfo( Variant::BOOL, "value")));
ADD_SIGNAL( MethodInfo("resource_selected", PropertyInfo( Variant::OBJECT, "res"),PropertyInfo( Variant::STRING, "prop") ) );
@@ -2966,30 +3488,50 @@ void PropertyEditor::set_show_categories(bool p_show) {
update_tree();
}
+void PropertyEditor::set_use_filter(bool p_use) {
+
+ if (p_use==use_filter)
+ return;
+
+ use_filter=p_use;
+ update_tree();
+}
+
+void PropertyEditor::register_text_enter(Node* p_line_edit) {
+
+ ERR_FAIL_NULL(p_line_edit);
+ search_box=p_line_edit->cast_to<LineEdit>();
+
+ if (search_box)
+ search_box->connect("text_changed",this,"_filter_changed");
+
+}
+
PropertyEditor::PropertyEditor() {
-
+
_prop_edited="property_edited";
_prop_edited_name.push_back(String());
undo_redo=NULL;
obj=NULL;
+ search_box=NULL;
changing=false;
update_tree_pending=false;
-
+
top_label = memnew( Label );
top_label->set_text("Properties:");
top_label->set_anchor( MARGIN_RIGHT, ANCHOR_END );
top_label->set_begin( Point2( 10,0) );
- top_label->set_end( Point2( 0,12) );
-
- add_child(top_label);
+ top_label->set_end( Point2( 0,12) );
+
+ add_child(top_label);
+
-
tree = memnew( Tree );
tree->set_anchor( MARGIN_RIGHT, ANCHOR_END );
- tree->set_anchor( MARGIN_BOTTOM, ANCHOR_END );
+ tree->set_anchor( MARGIN_BOTTOM, ANCHOR_END );
tree->set_begin( Point2(0,19 ));
tree->set_end( Point2(0,0 ));
-
+
tree->set_columns(2);
tree->set_column_expand(0,true);
tree->set_column_min_width(0,30);
@@ -2997,21 +3539,22 @@ PropertyEditor::PropertyEditor() {
tree->set_column_min_width(1,18);
//tree->set_hide_root(true);
- add_child( tree );
-
+ add_child( tree );
+
tree->connect("item_edited", this,"_item_edited",varray(),CONNECT_DEFERRED);
tree->connect("cell_selected", this,"_item_selected");
-
+
set_fixed_process(true);
-
+
custom_editor = memnew( CustomPropertyEditor );
add_child(custom_editor);
-
+
tree->connect("custom_popup_edited", this,"_custom_editor_request");
tree->connect("button_pressed", this,"_edit_button");
custom_editor->connect("variant_changed", this,"_custom_editor_edited");
custom_editor->connect("resource_edit_request", this,"_resource_edit_request",make_binds(),CONNECT_DEFERRED);
-
+ tree->set_hide_folding(true);
+
capitalize_paths=true;
autoclear=false;
tree->set_column_title(0,"Property");
@@ -3021,7 +3564,11 @@ PropertyEditor::PropertyEditor() {
keying=false;
read_only=false;
show_categories=false;
-
+ refresh_countdown=0;
+ use_doc_hints=false;
+
+ use_filter=false;
+
}
diff --git a/tools/editor/property_editor.h b/tools/editor/property_editor.h
index 08435ad75d..5fb8386b1b 100644
--- a/tools/editor/property_editor.h
+++ b/tools/editor/property_editor.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,7 +32,7 @@
#include "scene/gui/tree.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
-#include "scene/gui/file_dialog.h"
+#include "tools/editor/editor_file_dialog.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/color_picker.h"
#include "scene/gui/menu_button.h"
@@ -46,9 +46,9 @@
*/
class CustomPropertyEditor : public Popup {
-
+
OBJ_TYPE( CustomPropertyEditor, Popup );
-
+
enum {
MAX_VALUE_EDITORS=12,
MAX_ACTION_BUTTONS=5,
@@ -75,7 +75,7 @@ class CustomPropertyEditor : public Popup {
PopupMenu *menu;
SceneTreeDialog *scene_tree;
- FileDialog *file;
+ EditorFileDialog *file;
ConfirmationDialog *error;
String name;
Variant::Type type;
@@ -93,6 +93,8 @@ class CustomPropertyEditor : public Popup {
TextEdit *text_edit;
bool read_only;
Button *checks20[20];
+ SpinBox *spinbox;
+ HSlider *slider;
Control *easing_draw;
@@ -105,6 +107,7 @@ class CustomPropertyEditor : public Popup {
void _file_selected(String p_file);
void _scroll_modified(double p_value);
void _modified(String p_string);
+ void _range_modified(double p_value);
void _focus_enter();
void _focus_exit();
void _action_pressed(int p_which);
@@ -113,36 +116,38 @@ class CustomPropertyEditor : public Popup {
void _color_changed(const Color& p_color);
void _draw_easing();
void _menu_option(int p_which);
-
+
void _drag_easing(const InputEvent& p_ev);
void _node_path_selected(NodePath p_path);
void show_value_editors(int p_amount);
void config_value_editors(int p_amount, int p_columns,int p_label_w,const List<String>& p_strings);
void config_action_buttons(const List<String>& p_strings);
+
protected:
void _notification(int p_what);
- static void _bind_methods();
-
-public:
+ static void _bind_methods();
+
+public:
Variant get_variant() const;
String get_name() const;
-
+
void set_read_only(bool p_read_only) { read_only=p_read_only; }
bool edit(Object* p_owner,const String& p_name,Variant::Type p_type, const Variant& p_variant,int p_hint,String p_hint_text);
-
+
CustomPropertyEditor();
};
class PropertyEditor : public Control {
-
+
OBJ_TYPE( PropertyEditor, Control );
-
+
Tree *tree;
Label *top_label;
//Object *object;
+ LineEdit *search_box;
Object* obj;
@@ -156,39 +161,54 @@ class PropertyEditor : public Control {
bool keying;
bool read_only;
bool show_categories;
+ float refresh_countdown;
+ bool use_doc_hints;
+
+ bool use_filter;
HashMap<String,String> pending;
String selected_property;
+
+ Map<StringName,Map<StringName,String> > descr_cache;
+ Map<StringName,String > class_descr_cache;
CustomPropertyEditor *custom_editor;
-
+
void _resource_edit_request();
void _custom_editor_edited();
void _custom_editor_request(bool p_arrow);
-
+
void _item_selected();
void _item_edited();
TreeItem *get_parent_node(String p_path,HashMap<String,TreeItem*>& item_paths,TreeItem *root);
-
+
void set_item_text(TreeItem *p_item, int p_type, const String& p_name, int p_hint=PROPERTY_HINT_NONE, const String& p_hint_text="");
-
+
TreeItem *find_item(TreeItem *p_item,const String& p_name);
-
-
+
+
virtual void _changed_callback(Object *p_changed,const char * p_what);
virtual void _changed_callbacks(Object *p_changed,const String& p_callback);
+
void _edit_button(Object *p_item, int p_column, int p_button);
-
+
void _node_removed(Node *p_node);
void _edit_set(const String& p_name, const Variant& p_value);
void _draw_flags(Object *ti,const Rect2& p_rect);
- Node *get_instanced_node();
+ bool _might_be_in_instance();
+ bool _get_instanced_node_original_property(const StringName& p_prop,Variant& value);
+ bool _is_property_different(const Variant& p_current, const Variant& p_orig,int p_usage=0);
+
+ void _refresh_item(TreeItem *p_item);
+ void _set_range_def(Object *p_item, String prop, float p_frame);
+
+ void _filter_changed(const String& p_text);
UndoRedo *undo_redo;
protected:
-
+
void _notification(int p_what);
static void _bind_methods();
public:
@@ -203,6 +223,8 @@ public:
void update_tree();
void update_property(const String& p_prop);
+ void refresh();
+
void edit(Object* p_object);
void set_keying(bool p_active);
@@ -212,7 +234,11 @@ public:
void set_autoclear(bool p_enable);
void set_show_categories(bool p_show);
-
+ void set_use_doc_hints(bool p_enable) { use_doc_hints=p_enable; }
+
+ void set_use_filter(bool p_use);
+ void register_text_enter(Node *p_line_edit);
+
PropertyEditor();
~PropertyEditor();
diff --git a/tools/editor/pvrtc_compress.cpp b/tools/editor/pvrtc_compress.cpp
index 9511d6a26d..a2f98adbe0 100644
--- a/tools/editor/pvrtc_compress.cpp
+++ b/tools/editor/pvrtc_compress.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,11 +32,34 @@
#include "io/resource_saver.h"
#include "io/resource_loader.h"
#include "os/os.h"
-
+#include "os/file_access.h"
+static void (*_base_image_compress_pvrtc2_func)(Image *)=NULL;
+static void (*_base_image_compress_pvrtc4_func)(Image *)=NULL;
static void _compress_image(Image::CompressMode p_mode,Image *p_image) {
String ttpath = EditorSettings::get_singleton()->get("PVRTC/texture_tool");
+
+ if (ttpath.strip_edges()=="" || !FileAccess::exists(ttpath)) {
+ switch(p_mode) {
+
+ case Image::COMPRESS_PVRTC2:
+ if (_base_image_compress_pvrtc2_func)
+ _base_image_compress_pvrtc2_func(p_image);
+ else if (_base_image_compress_pvrtc4_func)
+ _base_image_compress_pvrtc4_func(p_image);
+
+ break;
+ case Image::COMPRESS_PVRTC4:
+ if (_base_image_compress_pvrtc4_func)
+ _base_image_compress_pvrtc4_func(p_image);
+
+ break;
+ default: ERR_FAIL();
+
+ }
+ return;
+ }
String spath = EditorSettings::get_singleton()->get_settings_path();
@@ -100,6 +123,9 @@ static void _compress_etc(Image *p_image) {
void _pvrtc_register_compressors() {
+ _base_image_compress_pvrtc2_func=Image::_image_compress_pvrtc2_func;
+ _base_image_compress_pvrtc4_func=Image::_image_compress_pvrtc4_func;
+
Image::_image_compress_pvrtc2_func=_compress_pvrtc2;
Image::_image_compress_pvrtc4_func=_compress_pvrtc4;
//Image::_image_compress_etc_func=_compress_etc; //use the built in one for ETC
diff --git a/tools/editor/pvrtc_compress.h b/tools/editor/pvrtc_compress.h
index e683ed7804..c4fb0bacb5 100644
--- a/tools/editor/pvrtc_compress.h
+++ b/tools/editor/pvrtc_compress.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/quick_open.cpp b/tools/editor/quick_open.cpp
index 129c637ab0..6135a4ab64 100644
--- a/tools/editor/quick_open.cpp
+++ b/tools/editor/quick_open.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,8 +30,9 @@
#include "os/keyboard.h"
-void EditorQuickOpen::popup(const String& p_base, bool p_dontclear) {
+void EditorQuickOpen::popup(const StringName &p_base, bool p_dontclear, bool p_add_dirs) {
+ add_directories=p_add_dirs;
popup_centered_ratio(0.6);
if (p_dontclear)
search_box->select_all();
@@ -66,27 +67,53 @@ void EditorQuickOpen::_sbox_input(const InputEvent& p_ie) {
void EditorQuickOpen::_parse_fs(EditorFileSystemDirectory *efsd) {
- for(int i=0;i<efsd->get_subdir_count();i++) {
+ if (!add_directories) {
+ for(int i=0;i<efsd->get_subdir_count();i++) {
- _parse_fs(efsd->get_subdir(i));
+ _parse_fs(efsd->get_subdir(i));
+ }
}
+ TreeItem *root = search_options->get_root();
+
+ if (add_directories) {
+ String path = efsd->get_path();
+ if (!path.ends_with("/"))
+ path+="/";
+ if (path!="res://") {
+ path=path.substr(6,path.length());
+ if (path.findn(search_box->get_text())!=-1) {
+ TreeItem *ti = search_options->create_item(root);
+ ti->set_text(0,path);
+ Ref<Texture> icon = get_icon("folder","FileDialog");
+ ti->set_icon(0,icon);
+ }
+ }
+ }
for(int i=0;i<efsd->get_file_count();i++) {
String file = efsd->get_file_path(i);
file=file.substr(6,file.length());
if (ObjectTypeDB::is_type(efsd->get_file_type(i),base_type) && (search_box->get_text()=="" || file.findn(search_box->get_text())!=-1)) {
- TreeItem *root = search_options->get_root();
TreeItem *ti = search_options->create_item(root);
ti->set_text(0,file);
- Ref<Texture> icon = get_icon( (has_icon(efsd->get_file_type(i),"EditorIcons")?efsd->get_file_type(i):String("Object")),"EditorIcons");
+ Ref<Texture> icon = get_icon( (has_icon(efsd->get_file_type(i),ei)?efsd->get_file_type(i):ot),ei);
ti->set_icon(0,icon);
if (root->get_children()==ti)
ti->select(0);
}
}
+
+
+ if (add_directories) {
+ for(int i=0;i<efsd->get_subdir_count();i++) {
+
+ _parse_fs(efsd->get_subdir(i));
+ }
+ }
+
}
void EditorQuickOpen::_update_search() {
@@ -118,7 +145,7 @@ void EditorQuickOpen::_notification(int p_what) {
}
-String EditorQuickOpen::get_base_type() const {
+StringName EditorQuickOpen::get_base_type() const {
return base_type;
}
@@ -152,4 +179,7 @@ EditorQuickOpen::EditorQuickOpen() {
set_hide_on_ok(false);
search_options->connect("item_activated",this,"_confirmed");
search_options->set_hide_root(true);
+ ei="EditorIcons";
+ ot="Object";
+ add_directories=false;
}
diff --git a/tools/editor/quick_open.h b/tools/editor/quick_open.h
index 9d8a7a1abd..8b38256d39 100644
--- a/tools/editor/quick_open.h
+++ b/tools/editor/quick_open.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -38,7 +38,11 @@ class EditorQuickOpen : public ConfirmationDialog {
LineEdit *search_box;
Tree *search_options;
- String base_type;
+ StringName base_type;
+ StringName ei;
+ StringName ot;
+ bool add_directories;
+
void _update_search();
@@ -55,9 +59,9 @@ protected:
static void _bind_methods();
public:
- String get_base_type() const;
+ StringName get_base_type() const;
- void popup(const String& p_base,bool p_dontclear=false);
+ void popup(const StringName& p_base,bool p_dontclear=false,bool p_add_dirs=false);
EditorQuickOpen();
};
diff --git a/tools/editor/register_exporters.h b/tools/editor/register_exporters.h
index 559f0a9200..0e1ad2ca46 100644
--- a/tools/editor/register_exporters.h
+++ b/tools/editor/register_exporters.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/reparent_dialog.cpp b/tools/editor/reparent_dialog.cpp
index 5a5566c756..78ba47d54b 100644
--- a/tools/editor/reparent_dialog.cpp
+++ b/tools/editor/reparent_dialog.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -41,6 +41,11 @@ void ReparentDialog::_notification(int p_what) {
connect("confirmed", this,"_reparent");
}
+ if (p_what==NOTIFICATION_EXIT_TREE) {
+
+ disconnect("confirmed", this,"_reparent");
+ }
+
if (p_what==NOTIFICATION_DRAW) {
//RID ci = get_canvas_item();
@@ -98,6 +103,7 @@ ReparentDialog::ReparentDialog() {
add_child(node_only);
node_only->hide();
+ tree->set_show_enabled_subscene(true);
//vbc->add_margin_child("Options:",node_only);;
diff --git a/tools/editor/reparent_dialog.h b/tools/editor/reparent_dialog.h
index 52a2192d7c..78c0df9285 100644
--- a/tools/editor/reparent_dialog.h
+++ b/tools/editor/reparent_dialog.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/resources_dock.cpp b/tools/editor/resources_dock.cpp
index 4614c4945d..b69eec4a51 100644
--- a/tools/editor/resources_dock.cpp
+++ b/tools/editor/resources_dock.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -134,7 +134,7 @@ void ResourcesDock::save_resource(const String& p_path,const Ref<Resource>& p_re
if (err!=OK) {
accept->set_text("Error saving resource!");
- accept->popup_centered(Size2(300,100));
+ accept->popup_centered_minsize();
return;
}
// EditorFileSystem::get_singleton()->update_file(path,p_resource->get_type());
@@ -152,7 +152,7 @@ void ResourcesDock::save_resource_as(const Ref<Resource>& p_resource) {
List<String> extensions;
ResourceSaver::get_recognized_extensions(res,&extensions);
- file->set_mode(FileDialog::MODE_SAVE_FILE);
+ file->set_mode(EditorFileDialog::MODE_SAVE_FILE);
if (p_resource->get_path()!="" && p_resource->get_path().find("::")==-1) {
@@ -396,7 +396,7 @@ ResourcesDock::ResourcesDock(EditorNode *p_editor) {
accept = memnew (AcceptDialog);
add_child(accept);
- file = memnew( FileDialog );
+ file = memnew( EditorFileDialog );
add_child(file);
file->connect("file_selected",this,"_file_action");
diff --git a/tools/editor/resources_dock.h b/tools/editor/resources_dock.h
index edf769b157..933b457b29 100644
--- a/tools/editor/resources_dock.h
+++ b/tools/editor/resources_dock.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -38,6 +38,7 @@
#include "scene/gui/menu_button.h"
#include "scene/gui/file_dialog.h"
#include "create_dialog.h"
+#include "editor_file_dialog.h"
class EditorNode;
@@ -68,7 +69,7 @@ class ResourcesDock : public VBoxContainer {
CreateDialog *create_dialog;
AcceptDialog *accept;
- FileDialog *file;
+ EditorFileDialog *file;
Tree *resources;
bool block_add;
int current_action;
diff --git a/tools/editor/run_settings_dialog.cpp b/tools/editor/run_settings_dialog.cpp
index efe9c8f05f..e883c69939 100644
--- a/tools/editor/run_settings_dialog.cpp
+++ b/tools/editor/run_settings_dialog.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/run_settings_dialog.h b/tools/editor/run_settings_dialog.h
index da7c818263..fdb8857f6b 100644
--- a/tools/editor/run_settings_dialog.h
+++ b/tools/editor/run_settings_dialog.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/scene_tree_dock.cpp b/tools/editor/scene_tree_dock.cpp
index f5d9e83bf8..0cafe7459b 100644
--- a/tools/editor/scene_tree_dock.cpp
+++ b/tools/editor/scene_tree_dock.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,11 +33,12 @@
#include "scene/resources/packed_scene.h"
#include "editor_settings.h"
#include "tools/editor/plugins/canvas_item_editor_plugin.h"
-
+#include "script_editor_debugger.h"
+#include "tools/editor/plugins/script_editor_plugin.h"
+#include "multi_node_edit.h"
void SceneTreeDock::_unhandled_key_input(InputEvent p_event) {
-
uint32_t sc = p_event.key.get_scancode_with_modifiers();
if (!p_event.key.pressed || p_event.key.echo)
return;
@@ -61,7 +62,7 @@ Node* SceneTreeDock::instance(const String& p_file) {
//accept->get_cancel()->hide();
accept->get_ok()->set_text("Ok :( ");
accept->set_text("No parent to instance a child at.");
- accept->popup_centered(Size2(300,70));
+ accept->popup_centered_minsize();
return NULL;
};
@@ -70,7 +71,7 @@ Node* SceneTreeDock::instance(const String& p_file) {
Node*instanced_scene=NULL;
Ref<PackedScene> sdata = ResourceLoader::load(p_file);
if (sdata.is_valid())
- instanced_scene=sdata->instance();
+ instanced_scene=sdata->instance(true);
if (!instanced_scene) {
@@ -79,11 +80,23 @@ Node* SceneTreeDock::instance(const String& p_file) {
//accept->get_cancel()->hide();
accept->get_ok()->set_text("Ugh");
accept->set_text(String("Error loading scene from ")+p_file);
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
return NULL;
}
- instanced_scene->generate_instance_state();
+ // If the scene hasn't been saved yet a cyclical dependency cannot exist.
+ if (edited_scene->get_filename()!="") {
+
+ if (_cyclical_dependency_exists(edited_scene->get_filename(), instanced_scene)) {
+
+ accept->get_ok()->set_text("Ok");
+ accept->set_text(String("Cannot instance the scene '")+p_file+String("' because the current scene exists within one of its' nodes."));
+ accept->popup_centered_minsize();
+ return NULL;
+ }
+ }
+
+ //instanced_scene->generate_instance_state();
instanced_scene->set_filename( Globals::get_singleton()->localize_path(p_file) );
editor_data->get_undo_redo().create_action("Instance Scene");
@@ -93,6 +106,13 @@ Node* SceneTreeDock::instance(const String& p_file) {
editor_data->get_undo_redo().add_do_method(editor_selection,"add_node",instanced_scene);
editor_data->get_undo_redo().add_do_reference(instanced_scene);
editor_data->get_undo_redo().add_undo_method(parent,"remove_child",instanced_scene);
+
+
+ String new_name = parent->validate_child_name(instanced_scene->get_name());
+ ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
+ editor_data->get_undo_redo().add_do_method(sed,"live_debug_instance_node",edited_scene->get_path_to(parent),p_file,new_name);
+ editor_data->get_undo_redo().add_undo_method(sed,"live_debug_remove_node",NodePath(String(edited_scene->get_path_to(parent))+"/"+new_name));
+
editor_data->get_undo_redo().commit_action();
@@ -100,6 +120,35 @@ Node* SceneTreeDock::instance(const String& p_file) {
}
+bool SceneTreeDock::_cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node) {
+ int childCount = p_desired_node->get_child_count();
+
+ if (p_desired_node->get_filename()==p_target_scene_path) {
+ return true;
+ }
+
+ for (int i=0;i<childCount;i++) {
+ Node* child=p_desired_node->get_child(i);
+
+ if(_cyclical_dependency_exists(p_target_scene_path,child)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+static String _get_name_num_separator() {
+ switch(EditorSettings::get_singleton()->get("scenetree_editor/duplicate_node_name_num_separator").operator int()) {
+ case 0: return "";
+ case 1: return " ";
+ case 2: return "_";
+ case 3: return "-";
+ }
+ return " ";
+}
+
void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
current_option=p_tool;
@@ -109,8 +158,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
case TOOL_NEW: {
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
create_dialog->popup_centered_ratio();
} break;
case TOOL_INSTANCE: {
@@ -123,14 +172,14 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("This operation can't be done without a tree root.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
- file->set_mode(FileDialog::MODE_OPEN_FILE);
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions);
file->clear_filters();
@@ -153,8 +202,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (!current)
break;
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
connect_dialog->popup_centered_ratio();
connect_dialog->set_node(current);
@@ -164,8 +213,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
Node *current = scene_tree->get_selected();
if (!current)
break;
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
groups_editor->set_current(current);
groups_editor->popup_centered_ratio();
} break;
@@ -175,8 +224,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (!selected)
break;
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
Ref<Script> existing = selected->get_script();
if (existing.is_valid())
@@ -204,7 +253,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
//accept->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("This operation can't be done on the tree root.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
@@ -272,7 +321,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
//accept->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("This operation can't be done on the tree root.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
@@ -318,17 +367,21 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
}
int num=nums.to_int();
- if (num<2)
- num=2;
+ if (num<1)
+ num=1;
else
num++;
- name=name.substr(0,name.length()-nums.length()).strip_edges();
- String attempt=name+" "+itos(num);
+ String nnsep = _get_name_num_separator();
+ name = name.substr(0,name.length()-nums.length()).strip_edges();
+ if ( name.substr(name.length()-nnsep.length(),nnsep.length()) == nnsep) {
+ name = name.substr(0,name.length()-nnsep.length());
+ }
+ String attempt = (name + nnsep + itos(num)).strip_edges();
while(parent->has_node(attempt)) {
num++;
- attempt=name+" "+itos(num);
+ attempt = (name + nnsep + itos(num)).strip_edges();
}
dup->set_name(attempt);
@@ -344,9 +397,14 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
editor_data->get_undo_redo().add_do_method(d,"set_owner",node->get_owner());
}
editor_data->get_undo_redo().add_do_method(editor_selection,"add_node",dup);
- editor_data->get_undo_redo().add_undo_method(parent,"remove_child",dup);
+ editor_data->get_undo_redo().add_undo_method(parent,"remove_child",dup);
editor_data->get_undo_redo().add_do_reference(dup);
+ ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
+
+ editor_data->get_undo_redo().add_do_method(sed,"live_debug_duplicate_node",edited_scene->get_path_to(node),attempt);
+ editor_data->get_undo_redo().add_undo_method(sed,"live_debug_remove_node",NodePath(String(edited_scene->get_path_to(parent))+"/"+attempt));
+
//parent->add_child(dup);
//reselect.push_back(dup);
}
@@ -375,7 +433,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
//confirmation->get_cancel()->hide();
accept->get_ok()->set_text("I see..");
accept->set_text("This operation can't be done on the tree root.");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
break;
}
@@ -392,6 +450,19 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
reparent_dialog->set_current( nodeset );
} break;
+ case TOOL_MULTI_EDIT: {
+
+ Node*root=EditorNode::get_singleton()->get_edited_scene();
+ if (!root)
+ break;
+ Ref<MultiNodeEdit> mne = memnew( MultiNodeEdit );
+ for (const Map<Node*,Object*>::Element *E=EditorNode::get_singleton()->get_editor_selection()->get_selection().front();E;E=E->next()) {
+ mne->add_node(root->get_path_to(E->key()));
+ }
+
+ EditorNode::get_singleton()->push_item(mne.ptr());
+
+ } break;
case TOOL_ERASE: {
List<Node*> remove_list = editor_selection->get_selected_node_list();
@@ -411,7 +482,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
} else {
delete_dialog->set_text("Delete Node(s)?");
- delete_dialog->popup_centered(Size2(200,80));
+ delete_dialog->popup_centered_minsize();
}
@@ -426,8 +497,18 @@ void SceneTreeDock::_notification(int p_what) {
switch(p_what) {
- case NOTIFICATION_ENTER_TREE: {
+ case NOTIFICATION_READY: {
+
+ if (!first_enter)
+ break;
+ first_enter=false;
+ CanvasItemEditorPlugin *canvas_item_plugin = editor_data->get_editor("2D")->cast_to<CanvasItemEditorPlugin>();
+ if (canvas_item_plugin) {
+ canvas_item_plugin->get_canvas_item_editor()->connect("item_lock_status_changed", scene_tree, "_update_tree");
+ canvas_item_plugin->get_canvas_item_editor()->connect("item_group_status_changed", scene_tree, "_update_tree");
+ scene_tree->connect("node_changed", canvas_item_plugin->get_canvas_item_editor()->get_viewport_control(), "update");
+ }
static const char* button_names[TOOL_BUTTON_MAX]={
"New",
"Add",
@@ -439,21 +520,17 @@ void SceneTreeDock::_notification(int p_what) {
"MoveDown",
"Duplicate",
"Reparent",
- "Del",
+ "MultiNodeEdit",
+ "Remove",
};
+
+
for(int i=0;i<TOOL_BUTTON_MAX;i++)
tool_buttons[i]->set_icon(get_icon(button_names[i],"EditorIcons"));
- } break;
- case NOTIFICATION_READY: {
+ EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed",this,"_selection_changed");
- CanvasItemEditorPlugin *canvas_item_plugin = editor_data->get_editor("2D")->cast_to<CanvasItemEditorPlugin>();
- if (canvas_item_plugin) {
- canvas_item_plugin->get_canvas_item_editor()->connect("item_lock_status_changed", scene_tree, "_update_tree");
- canvas_item_plugin->get_canvas_item_editor()->connect("item_group_status_changed", scene_tree, "_update_tree");
- scene_tree->connect("node_changed", canvas_item_plugin->get_canvas_item_editor()->get_viewport_control(), "update");
- }
} break;
}
}
@@ -496,9 +573,9 @@ Node *SceneTreeDock::_duplicate(Node *p_node, Map<Node*,Node*> &duplimap) {
Ref<PackedScene> sd = ResourceLoader::load( p_node->get_filename() );
ERR_FAIL_COND_V(!sd.is_valid(),NULL);
- node = sd->instance();
+ node = sd->instance(true);
ERR_FAIL_COND_V(!node,NULL);
- node->generate_instance_state();
+ //node->generate_instance_state();
} else {
Object *obj = ObjectTypeDB::instance(p_node->get_type());
ERR_FAIL_COND_V(!obj,NULL);
@@ -793,10 +870,20 @@ bool SceneTreeDock::_validate_no_foreign() {
accept->get_ok()->set_text("Makes Sense!");
accept->set_text("Can't operate on nodes from a foreign scene!");
- accept->popup_centered(Size2(300,70));;
+ accept->popup_centered_minsize();
+ return false;
+
+ }
+
+ if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(E->get()))>=0) {
+
+ accept->get_ok()->set_text("Makes Sense!");
+ accept->set_text("Can't operate on nodes the current scene inherits from!");
+ accept->popup_centered_minsize();
return false;
}
+
}
return true;
@@ -855,6 +942,13 @@ void SceneTreeDock::_node_reparent(NodePath p_path,bool p_node_only) {
editor_data->get_undo_redo().add_do_method(node->get_parent(),"remove_child",node);
editor_data->get_undo_redo().add_do_method(new_parent,"add_child",node);
+
+ ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
+ String new_name = new_parent->validate_child_name(node->get_name());
+ editor_data->get_undo_redo().add_do_method(sed,"live_debug_reparent_node",edited_scene->get_path_to(node),edited_scene->get_path_to(new_parent),new_name,-1);
+ editor_data->get_undo_redo().add_undo_method(sed,"live_debug_reparent_node",NodePath(String(edited_scene->get_path_to(new_parent))+"/"+new_name),edited_scene->get_path_to(node->get_parent()),node->get_name(),node->get_index());
+
+
editor_data->get_undo_redo().add_do_method(this,"_set_owners",edited_scene,owners);
if (editor->get_animation_editor()->get_root()==node)
@@ -977,6 +1071,11 @@ void SceneTreeDock::_delete_confirm() {
editor_data->get_undo_redo().add_undo_method(this,"_set_owners",edited_scene,owners);
//editor_data->get_undo_redo().add_undo_method(n,"set_owner",n->get_owner());
editor_data->get_undo_redo().add_undo_reference(n);
+
+ ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
+ editor_data->get_undo_redo().add_do_method(sed,"live_debug_remove_and_keep_node",edited_scene->get_path_to(n),n->get_instance_ID());
+ editor_data->get_undo_redo().add_undo_method(sed,"live_debug_restore_node",n->get_instance_ID(),edited_scene->get_path_to(n->get_parent()),n->get_index());
+
}
@@ -990,21 +1089,33 @@ void SceneTreeDock::_update_tool_buttons() {
Node *sel = scene_tree->get_selected();
bool disable = !sel || (sel!=edited_scene && sel->get_owner()!=edited_scene);
+ disable = disable || (edited_scene->get_scene_inherited_state().is_valid() && edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(sel))>=0);
bool disable_root = disable || sel->get_parent()==scene_root;
+ bool disable_edit = !sel;
- tool_buttons[TOOL_INSTANCE]->set_disabled(disable);
+ tool_buttons[TOOL_INSTANCE]->set_disabled(disable_edit);
tool_buttons[TOOL_REPLACE]->set_disabled(disable);
- tool_buttons[TOOL_CONNECT]->set_disabled(disable);
- tool_buttons[TOOL_GROUP]->set_disabled(disable);
- tool_buttons[TOOL_SCRIPT]->set_disabled(disable);
+ tool_buttons[TOOL_CONNECT]->set_disabled(disable_edit);
+ tool_buttons[TOOL_GROUP]->set_disabled(disable_edit);
+ tool_buttons[TOOL_SCRIPT]->set_disabled(disable_edit);
tool_buttons[TOOL_MOVE_UP]->set_disabled(disable_root);
tool_buttons[TOOL_MOVE_DOWN]->set_disabled(disable_root);
tool_buttons[TOOL_DUPLICATE]->set_disabled(disable_root);
tool_buttons[TOOL_REPARENT]->set_disabled(disable_root);
tool_buttons[TOOL_ERASE]->set_disabled(disable);
+ tool_buttons[TOOL_MULTI_EDIT]->set_disabled(EditorNode::get_singleton()->get_editor_selection()->get_selection().size()<2);
+
}
+
+void SceneTreeDock::_selection_changed() {
+
+ tool_buttons[TOOL_MULTI_EDIT]->set_disabled(EditorNode::get_singleton()->get_editor_selection()->get_selection().size()<2);
+
+}
+
+
void SceneTreeDock::_create() {
@@ -1014,14 +1125,15 @@ void SceneTreeDock::_create() {
if (edited_scene) {
-
+ // If root exists in edited scene
parent = scene_tree->get_selected();
- ERR_FAIL_COND(!parent);
- } else {
+ if( !parent )
+ parent = edited_scene;
+ } else {
+ // If no root exist in edited scene
parent = scene_root;
ERR_FAIL_COND(!parent);
-
}
Object *c = create_dialog->instance_selected();
@@ -1033,12 +1145,20 @@ void SceneTreeDock::_create() {
editor_data->get_undo_redo().create_action("Create Node");
if (edited_scene) {
+
editor_data->get_undo_redo().add_do_method(parent,"add_child",child);
editor_data->get_undo_redo().add_do_method(child,"set_owner",edited_scene);
editor_data->get_undo_redo().add_do_method(editor_selection,"clear");
editor_data->get_undo_redo().add_do_method(editor_selection,"add_node",child);
editor_data->get_undo_redo().add_do_reference(child);
editor_data->get_undo_redo().add_undo_method(parent,"remove_child",child);
+
+
+ String new_name = parent->validate_child_name(child->get_type());
+ ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
+ editor_data->get_undo_redo().add_do_method(sed,"live_debug_create_node",edited_scene->get_path_to(parent),child->get_type(),new_name);
+ editor_data->get_undo_redo().add_undo_method(sed,"live_debug_remove_node",NodePath(String(edited_scene->get_path_to(parent))+"/"+new_name));
+
} else {
editor_data->get_undo_redo().add_do_method(editor,"set_edited_scene",child);
@@ -1103,24 +1223,21 @@ void SceneTreeDock::_create() {
}
-
String newname=n->get_name();
n->replace_by(newnode,true);
-
if (n==edited_scene) {
edited_scene=newnode;
editor->set_edited_scene(newnode);
}
-
-
-
editor_data->get_undo_redo().clear_history();
- memdelete(n);
newnode->set_name(newname);
+
editor->push_item(newnode);
+ memdelete(n);
+
_update_tool_buttons();
}
@@ -1180,6 +1297,7 @@ void SceneTreeDock::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_delete_confirm"),&SceneTreeDock::_delete_confirm);
ObjectTypeDB::bind_method(_MD("_node_prerenamed"),&SceneTreeDock::_node_prerenamed);
ObjectTypeDB::bind_method(_MD("_import_subscene"),&SceneTreeDock::_import_subscene);
+ ObjectTypeDB::bind_method(_MD("_selection_changed"),&SceneTreeDock::_selection_changed);
ObjectTypeDB::bind_method(_MD("instance"),&SceneTreeDock::instance);
}
@@ -1209,7 +1327,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec
tb = memnew( ToolButton );
tb->connect("pressed",this,"_tool_selected",make_binds(TOOL_INSTANCE, false));
- tb->set_tooltip("Instance a Node from scene file.");
+ tb->set_tooltip("Instance a scene file as a Node.");
hbc_top->add_child(tb);
tool_buttons[TOOL_INSTANCE]=tb;
@@ -1283,6 +1401,12 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec
hbc_bottom->add_spacer();
tb = memnew( ToolButton );
+ tb->connect("pressed",this,"_tool_selected",make_binds(TOOL_MULTI_EDIT, false));
+ tb->set_tooltip("Multi-Edit Selected Nodes");
+ hbc_bottom->add_child(tb);
+ tool_buttons[TOOL_MULTI_EDIT]=tb;
+
+ tb = memnew( ToolButton );
tb->connect("pressed",this,"_tool_selected",make_binds(TOOL_ERASE, false));
tb->set_tooltip("Erase Selected Node(s)");
hbc_bottom->add_child(tb);
@@ -1309,7 +1433,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec
accept = memnew( AcceptDialog );
add_child(accept);
- file = memnew( FileDialog );
+ file = memnew( EditorFileDialog );
add_child(file);
file->connect("file_selected",this,"instance");
set_process_unhandled_key_input(true);
@@ -1321,7 +1445,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec
add_child(import_subscene_dialog);
import_subscene_dialog->connect("subscene_selected",this,"_import_subscene");
-
+ first_enter=true;
}
diff --git a/tools/editor/scene_tree_dock.h b/tools/editor/scene_tree_dock.h
index ac5391f3b9..b1c53d2ff9 100644
--- a/tools/editor/scene_tree_dock.h
+++ b/tools/editor/scene_tree_dock.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -62,6 +62,7 @@ class SceneTreeDock : public VBoxContainer {
TOOL_MOVE_DOWN,
TOOL_DUPLICATE,
TOOL_REPARENT,
+ TOOL_MULTI_EDIT,
TOOL_ERASE,
TOOL_BUTTON_MAX
};
@@ -87,9 +88,10 @@ class SceneTreeDock : public VBoxContainer {
ConfirmationDialog *delete_dialog;
ReparentDialog *reparent_dialog;
- FileDialog *file;
+ EditorFileDialog *file;
EditorSubScene *import_subscene_dialog;
+ bool first_enter;
void _create();
Node *scene_root;
@@ -102,6 +104,7 @@ class SceneTreeDock : public VBoxContainer {
void _load_request(const String& p_path);
void _script_open_request(const Ref<Script>& p_script);
+ bool _cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node);
void _node_selected();
void _node_renamed();
@@ -117,6 +120,7 @@ class SceneTreeDock : public VBoxContainer {
void _import_subscene();
bool _validate_no_foreign();
+ void _selection_changed();
void _fill_path_renames(Vector<StringName> base_path,Vector<StringName> new_base_path,Node * p_node, List<Pair<NodePath,NodePath> > *p_renames);
@@ -132,6 +136,7 @@ public:
void set_selected(Node *p_node, bool p_emit_selected=false);
void fill_path_renames(Node* p_node, Node *p_new_parent, List<Pair<NodePath,NodePath> > *p_renames);
void perform_node_renames(Node* p_base,List<Pair<NodePath,NodePath> > *p_renames, Map<Ref<Animation>, Set<int> > *r_rem_anims=NULL);
+ SceneTreeEditor *get_tree_editor() { return scene_tree; }
SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelection *p_editor_selection,EditorData &p_editor_data);
};
diff --git a/tools/editor/scene_tree_editor.cpp b/tools/editor/scene_tree_editor.cpp
index e9ec0199d0..ac2f76acdc 100644
--- a/tools/editor/scene_tree_editor.cpp
+++ b/tools/editor/scene_tree_editor.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -34,6 +34,8 @@
#include "scene/main/viewport.h"
#include "tools/editor/plugins/canvas_item_editor_plugin.h"
+#include "scene/resources/packed_scene.h"
+
Node *SceneTreeEditor::get_scene_node() {
ERR_FAIL_COND_V(!is_inside_tree(),NULL);
@@ -57,22 +59,58 @@ void SceneTreeEditor::_subscene_option(int p_idx) {
switch(p_idx) {
- case SCENE_MENU_SHOW_CHILDREN: {
+ case SCENE_MENU_EDITABLE_CHILDREN: {
- if (node->has_meta("__editor_show_subtree")) {
- instance_menu->set_item_checked(0,true);
- node->set_meta("__editor_show_subtree",Variant());
- _update_tree();
- } else {
- node->set_meta("__editor_show_subtree",true);
- _update_tree();
+ bool editable = EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(node);
+ editable = !editable;
+
+ //node->set_instance_children_editable(editable);
+ EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node,editable);
+ instance_menu->set_item_checked(0,editable);
+ if (editable) {
+ node->set_scene_instance_load_placeholder(false);
+ instance_menu->set_item_checked(1,false);
+ }
+
+ _update_tree();
+
+ } break;
+ case SCENE_MENU_USE_PLACEHOLDER: {
+
+ bool placeholder = node->get_scene_instance_load_placeholder();
+ placeholder = !placeholder;
+
+ //node->set_instance_children_editable(editable);
+ if (placeholder) {
+ EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node,false);
}
+ node->set_scene_instance_load_placeholder(placeholder);
+ instance_menu->set_item_checked(0,false);
+ instance_menu->set_item_checked(1,placeholder);
+
+ _update_tree();
} break;
case SCENE_MENU_OPEN: {
emit_signal("open",node->get_filename());
} break;
+ case SCENE_MENU_CLEAR_INHERITANCE: {
+ clear_inherit_confirm->popup_centered_minsize();
+ } break;
+ case SCENE_MENU_OPEN_INHERITED: {
+ if (node && node->get_scene_inherited_state().is_valid()) {
+ emit_signal("open",node->get_scene_inherited_state()->get_path());
+ }
+ } break;
+ case SCENE_MENU_CLEAR_INHERITANCE_CONFIRM: {
+ if (node && node->get_scene_inherited_state().is_valid()) {
+ node->set_scene_inherited_state(Ref<SceneState>());
+ update_tree();
+ EditorNode::get_singleton()->get_property_editor()->update_tree();
+ }
+
+ } break;
}
@@ -94,15 +132,33 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item,int p_column,int p_id)
Rect2 item_rect = tree->get_item_rect(item,0);
item_rect.pos.y-=tree->get_scroll().y;
item_rect.pos+=tree->get_global_pos();
- instance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y));
- instance_menu->set_size(Vector2(item_rect.size.x,0));
- if (n->has_meta("__editor_show_subtree"))
- instance_menu->set_item_checked(0,true);
- else
- instance_menu->set_item_checked(0,false);
- instance_menu->popup();
- instance_node=n->get_instance_ID();
+ if (n==get_scene_node()) {
+ inheritance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y));
+ inheritance_menu->set_size(Vector2(item_rect.size.x,0));
+ inheritance_menu->popup();
+ instance_node=n->get_instance_ID();
+
+ } else {
+ instance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y));
+ instance_menu->set_size(Vector2(item_rect.size.x,0));
+ if (EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(n))
+ instance_menu->set_item_checked(0,true);
+ else
+ instance_menu->set_item_checked(0,false);
+
+ if (n->get_owner()==get_scene_node()) {
+ instance_menu->set_item_checked(1,n->get_scene_instance_load_placeholder());
+ instance_menu->set_item_disabled(1,false);
+ } else {
+
+ instance_menu->set_item_checked(1,false);
+ instance_menu->set_item_disabled(1,true);
+ }
+
+ instance_menu->popup();
+ instance_node=n->get_instance_ID();
+ }
//emit_signal("open",n->get_filename());
} else if (p_id==BUTTON_SCRIPT) {
RefPtr script=n->get_script();
@@ -117,7 +173,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item,int p_column,int p_id)
Spatial *ci = n->cast_to<Spatial>();
if (!ci->is_visible() && ci->get_parent_spatial() && !ci->get_parent_spatial()->is_visible()) {
error->set_text("This item cannot be made visible because the parent is hidden. Unhide the parent first.");
- error->popup_centered_minsize(Size2(400,80));
+ error->popup_centered_minsize();
return;
}
@@ -131,7 +187,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item,int p_column,int p_id)
CanvasItem *ci = n->cast_to<CanvasItem>();
if (!ci->is_visible() && ci->get_parent_item() && !ci->get_parent_item()->is_visible()) {
error->set_text("This item cannot be made visible because the parent is hidden. Unhide the parent first.");
- error->popup_centered_minsize(Size2(400,80));
+ error->popup_centered_minsize();
return;
}
bool v = !bool(n->call("is_hidden"));
@@ -168,20 +224,22 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) {
bool part_of_subscene=false;
- if (!display_foreign && p_node->get_owner()!=get_scene_node() && p_node!=get_scene_node()) {
+ if (!display_foreign && p_node->get_owner()!=get_scene_node() && p_node!=get_scene_node()) {
- if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && p_node->get_owner()->get_owner()==get_scene_node() && p_node->get_owner()->has_meta("__editor_show_subtree")) {
+ if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && (get_scene_node()->is_editable_instance(p_node->get_owner()))) {
part_of_subscene=true;
//allow
} else {
return;
}
+ } else {
+ part_of_subscene = get_scene_node()->get_scene_inherited_state().is_valid() && get_scene_node()->get_scene_inherited_state()->find_node_by_path(get_scene_node()->get_path_to(p_node))>=0;
}
TreeItem *item = tree->create_item(p_parent);
item->set_text(0, p_node->get_name() );
- if (can_rename && (p_node->get_owner() == get_scene_node() || p_node==get_scene_node()))
+ if (can_rename && !part_of_subscene /*(p_node->get_owner() == get_scene_node() || p_node==get_scene_node())*/)
item->set_editable(0, true);
item->set_selectable(0,true);
@@ -199,6 +257,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) {
icon=get_icon( (has_icon(p_node->get_type(),"EditorIcons")?p_node->get_type():String("Object")),"EditorIcons");
item->set_icon(0, icon );
item->set_metadata( 0,p_node->get_path() );
+
if (part_of_subscene) {
//item->set_selectable(0,marked_selectable);
@@ -221,7 +280,10 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) {
}
}
- if (p_node!=get_scene_node() && p_node->get_filename()!="" && can_open_instance) {
+ if (p_node==get_scene_node() && p_node->get_scene_inherited_state().is_valid()) {
+ item->add_button(0,get_icon("InstanceOptions","EditorIcons"),BUTTON_SUBSCENE);
+ item->set_tooltip(0,"Inherits: "+p_node->get_scene_inherited_state()->get_path()+"\nType: "+p_node->get_type());
+ } else if (p_node!=get_scene_node() && p_node->get_filename()!="" && can_open_instance) {
item->add_button(0,get_icon("InstanceOptions","EditorIcons"),BUTTON_SUBSCENE);
item->set_tooltip(0,"Instance: "+p_node->get_filename()+"\nType: "+p_node->get_type());
@@ -487,8 +549,11 @@ void SceneTreeEditor::_notification(int p_what) {
get_tree()->connect("tree_changed",this,"_tree_changed");
get_tree()->connect("node_removed",this,"_node_removed");
- instance_menu->set_item_icon(2,get_icon("Load","EditorIcons"));
+ instance_menu->set_item_icon(3,get_icon("Load","EditorIcons"));
tree->connect("item_collapsed",this,"_cell_collapsed");
+ inheritance_menu->set_item_icon(2,get_icon("Load","EditorIcons"));
+ clear_inherit_confirm->connect("confirmed",this,"_subscene_option",varray(SCENE_MENU_CLEAR_INHERITANCE_CONFIRM));
+
// get_scene()->connect("tree_changed",this,"_tree_changed",Vector<Variant>(),CONNECT_DEFERRED);
// get_scene()->connect("node_removed",this,"_node_removed",Vector<Variant>(),CONNECT_DEFERRED);
@@ -498,6 +563,8 @@ void SceneTreeEditor::_notification(int p_what) {
get_tree()->disconnect("tree_changed",this,"_tree_changed");
get_tree()->disconnect("node_removed",this,"_node_removed");
+ tree->disconnect("item_collapsed",this,"_cell_collapsed");
+ clear_inherit_confirm->disconnect("confirmed",this,"_subscene_option");
_update_tree();
}
@@ -582,20 +649,28 @@ void SceneTreeEditor::_rename_node(ObjectID p_node,const String& p_name) {
void SceneTreeEditor::_renamed() {
TreeItem *which=tree->get_edited();
-
+
ERR_FAIL_COND(!which);
NodePath np = which->get_metadata(0);
Node *n=get_node(np);
ERR_FAIL_COND(!n);
+ String new_name=which->get_text(0);
+ if (new_name.find(".") != -1 || new_name.find("/") != -1) {
+
+ error->set_text("Invalid node name, the following characters are not allowed:\n \".\", \"/\"");
+ error->popup_centered_minsize();
+ new_name=n->get_name();
+ }
+
if (!undo_redo) {
- n->set_name( which->get_text(0) );
+ n->set_name( new_name );
which->set_metadata(0,n->get_path());
emit_signal("node_renamed");
} else {
undo_redo->create_action("Rename Node");
- emit_signal("node_prerename",n,which->get_text(0));
- undo_redo->add_do_method(this,"_rename_node",n->get_instance_ID(),which->get_text(0));
+ emit_signal("node_prerename",n,new_name);
+ undo_redo->add_do_method(this,"_rename_node",n->get_instance_ID(),new_name);
undo_redo->add_undo_method(this,"_rename_node",n->get_instance_ID(),n->get_name());
undo_redo->commit_action();
}
@@ -787,12 +862,27 @@ SceneTreeEditor::SceneTreeEditor(bool p_label,bool p_can_rename, bool p_can_open
blocked=0;
instance_menu = memnew( PopupMenu );
- instance_menu->add_check_item("Show Children",SCENE_MENU_SHOW_CHILDREN);
+ instance_menu->add_check_item("Editable Children",SCENE_MENU_EDITABLE_CHILDREN);
+ instance_menu->add_check_item("Load As Placeholder",SCENE_MENU_USE_PLACEHOLDER);
instance_menu->add_separator();
instance_menu->add_item("Open in Editor",SCENE_MENU_OPEN);
instance_menu->connect("item_pressed",this,"_subscene_option");
add_child(instance_menu);
+ inheritance_menu = memnew( PopupMenu );
+ inheritance_menu->add_item("Clear Inheritance",SCENE_MENU_CLEAR_INHERITANCE);
+ inheritance_menu->add_separator();
+ inheritance_menu->add_item("Open in Editor",SCENE_MENU_OPEN_INHERITED);
+ inheritance_menu->connect("item_pressed",this,"_subscene_option");
+
+ add_child(inheritance_menu);
+
+ clear_inherit_confirm = memnew( ConfirmationDialog );
+ clear_inherit_confirm->set_text("Clear Inheritance? (No Undo!)");
+ clear_inherit_confirm->get_ok()->set_text("Clear!");
+ add_child(clear_inherit_confirm);
+
+
}
@@ -810,6 +900,11 @@ void SceneTreeDialog::_notification(int p_what) {
connect("confirmed", this,"_select");
}
+
+ if (p_what==NOTIFICATION_EXIT_TREE) {
+ disconnect("confirmed", this,"_select");
+
+ }
if (p_what==NOTIFICATION_DRAW) {
RID ci = get_canvas_item();
diff --git a/tools/editor/scene_tree_editor.h b/tools/editor/scene_tree_editor.h
index 5e88c5a41d..50cca4e24b 100644
--- a/tools/editor/scene_tree_editor.h
+++ b/tools/editor/scene_tree_editor.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -52,16 +52,22 @@ class SceneTreeEditor : public Control {
};
enum {
- SCENE_MENU_SHOW_CHILDREN,
+ SCENE_MENU_EDITABLE_CHILDREN,
+ SCENE_MENU_USE_PLACEHOLDER,
SCENE_MENU_OPEN,
+ SCENE_MENU_CLEAR_INHERITANCE,
+ SCENE_MENU_OPEN_INHERITED,
+ SCENE_MENU_CLEAR_INHERITANCE_CONFIRM,
};
Tree *tree;
Node *selected;
PopupMenu *instance_menu;
+ PopupMenu *inheritance_menu;
ObjectID instance_node;
AcceptDialog *error;
+ ConfirmationDialog *clear_inherit_confirm;
int blocked;
@@ -125,6 +131,9 @@ public:
void update_tree() { _update_tree(); }
+
+ Tree* get_scene_tree() { return tree; }
+
SceneTreeEditor(bool p_label=true,bool p_can_rename=false, bool p_can_open_instance=false);
~SceneTreeEditor();
@@ -150,7 +159,7 @@ protected:
static void _bind_methods();
public:
- SceneTreeEditor *get_tree() { return tree; }
+ SceneTreeEditor *get_scene_tree() { return tree; }
SceneTreeDialog();
~SceneTreeDialog();
diff --git a/tools/editor/scenes.cpp b/tools/editor/scenes.cpp
index 7860c04870..ada5751b5a 100644
--- a/tools/editor/scenes.cpp
+++ b/tools/editor/scenes.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/scenes.h b/tools/editor/scenes.h
index 59e7b728e2..463c3b5e18 100644
--- a/tools/editor/scenes.h
+++ b/tools/editor/scenes.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/tools/editor/scenes_dock.cpp b/tools/editor/scenes_dock.cpp
index 516cb5930d..c9b376ebec 100644
--- a/tools/editor/scenes_dock.cpp
+++ b/tools/editor/scenes_dock.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -35,15 +35,30 @@
#include "os/os.h"
#include "editor_node.h"
+#include "editor_settings.h"
+
bool ScenesDock::_create_tree(TreeItem *p_parent,EditorFileSystemDirectory *p_dir) {
- String search_term = tree_filter->get_search_term();
- ScenesDockFilter::FilterOption file_filter = tree_filter->get_file_filter();
TreeItem *item = tree->create_item(p_parent);
- item->set_text(0,p_dir->get_name()+"/");
+ String dname=p_dir->get_name();
+ if (dname=="")
+ dname="res://";
+
+ item->set_text(0,dname);
item->set_icon(0,get_icon("Folder","EditorIcons"));
- item->set_custom_bg_color(0,get_color("prop_subsection","Editor"));
+ item->set_selectable(0,true);
+ String lpath = p_dir->get_path();
+ if (lpath!="res://" && lpath.ends_with("/")) {
+ lpath=lpath.substr(0,lpath.length()-1);
+ }
+ item->set_metadata(0,lpath);
+ if (lpath==path) {
+ item->select(0);
+ }
+
+
+ //item->set_custom_bg_color(0,get_color("prop_subsection","Editor"));
bool has_items=false;
@@ -52,7 +67,7 @@ bool ScenesDock::_create_tree(TreeItem *p_parent,EditorFileSystemDirectory *p_di
if (_create_tree(item,p_dir->get_subdir(i)))
has_items=true;
}
-
+#if 0
for (int i=0;i<p_dir->get_file_count();i++) {
String file_name = p_dir->get_file(i);
@@ -89,13 +104,13 @@ bool ScenesDock::_create_tree(TreeItem *p_parent,EditorFileSystemDirectory *p_di
has_items=true;
}
-
- if (!has_items) {
+#endif
+ /*if (!has_items) {
memdelete(item);
return false;
- }
+ }*/
return true;
}
@@ -105,7 +120,28 @@ void ScenesDock::_update_tree() {
tree->clear();
updating_tree=true;
- _create_tree(NULL,EditorFileSystem::get_singleton()->get_filesystem());
+ TreeItem *root = tree->create_item();
+ TreeItem *favorites = tree->create_item(root);
+ favorites->set_icon(0, get_icon("Favorites","EditorIcons") );
+ favorites->set_text(0,"Favorites:");
+ favorites->set_selectable(0,false);
+ Vector<String> faves = EditorSettings::get_singleton()->get_favorite_dirs();
+ for(int i=0;i<faves.size();i++) {
+ if (!faves[i].begins_with("res://"))
+ continue;
+
+ TreeItem *ti = tree->create_item(favorites);
+ String fv = faves[i];
+ if (fv=="res://")
+ ti->set_text(0,"/");
+ else
+ ti->set_text(0,faves[i].get_file());
+ ti->set_icon(0,get_icon("Folder","EditorIcons"));
+ ti->set_selectable(0,true);
+ ti->set_metadata(0,faves[i]);
+ }
+
+ _create_tree(root,EditorFileSystem::get_singleton()->get_filesystem());
updating_tree=false;
}
@@ -117,68 +153,192 @@ void ScenesDock::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
+ if (initialized)
+ return;
+ initialized=true;
- EditorFileSystem::get_singleton()->connect("filesystem_changed",this,"_update_tree");
+ EditorFileSystem::get_singleton()->connect("filesystem_changed",this,"_fs_changed");
button_reload->set_icon( get_icon("Reload","EditorIcons"));
button_favorite->set_icon( get_icon("Favorites","EditorIcons"));
+ button_fav_up->set_icon( get_icon("MoveUp","EditorIcons"));
+ button_fav_down->set_icon( get_icon("MoveDown","EditorIcons"));
button_instance->set_icon( get_icon("Add","EditorIcons"));
button_open->set_icon( get_icon("Folder","EditorIcons"));
+ button_back->set_icon( get_icon("Filesystem","EditorIcons"));
+ display_mode->set_icon( get_icon("FileList","EditorIcons"));
+ display_mode->connect("pressed",this,"_change_file_display");
+ file_options->set_icon( get_icon("Tools","EditorIcons"));
+ files->connect("item_activated",this,"_select_file");
+ button_hist_next->connect("pressed",this,"_fw_history");
+ button_hist_prev->connect("pressed",this,"_bw_history");
- String path = Globals::get_singleton()->get_resource_path()+"/favorites.cfg";
- FileAccess *f=FileAccess::open(path,FileAccess::READ);
- if (f) {
-
- String l = f->get_line();
+ button_hist_next->set_icon( get_icon("Forward","EditorIcons"));
+ button_hist_prev->set_icon( get_icon("Back","EditorIcons"));
+ file_options->get_popup()->connect("item_pressed",this,"_file_option");
- while(l!="") {
- favorites.insert(l);
- l = f->get_line();
- }
+ button_back->connect("pressed",this,"_go_to_tree",varray(),CONNECT_DEFERRED);
+ current_path->connect("text_entered",this,"_go_to_dir");
+ _update_tree(); //maybe it finished already
- f->close();
- memdelete(f);
+ if (EditorFileSystem::get_singleton()->is_scanning()) {
+ _set_scannig_mode();
}
-
-
- _update_tree(); //maybe it finished already
+ } break;
+ case NOTIFICATION_PROCESS: {
+ if (EditorFileSystem::get_singleton()->is_scanning()) {
+ scanning_progress->set_val(EditorFileSystem::get_singleton()->get_scanning_progress()*100);
+ }
} break;
case NOTIFICATION_EXIT_TREE: {
} break;
- case NOTIFICATION_PROCESS: {
- } break;
}
}
-void ScenesDock::_favorite_toggled() {
- if (updating_tree)
+
+
+void ScenesDock::_dir_selected() {
+
+ TreeItem *ti = tree->get_selected();
+ if (!ti)
return;
+ String dir = ti->get_metadata(0);
+ bool found=false;
+ Vector<String> favorites = EditorSettings::get_singleton()->get_favorite_dirs();
+ for(int i=0;i<favorites.size();i++) {
+
+ if (favorites[i]==dir) {
+ found=true;
+ break;
+ }
+ }
+
+
+ button_favorite->set_pressed(found);
+ if (ti->get_parent() && ti->get_parent()->get_parent()==tree->get_root() && !ti->get_parent()->get_prev()) {
+
+ //a favorite!!!
+ button_fav_up->set_disabled(!ti->get_prev());
+ button_fav_down->set_disabled(!ti->get_next());
+ } else {
+ button_fav_up->set_disabled(true);
+ button_fav_down->set_disabled(true);
+
+ }
+
+}
+void ScenesDock::_fav_up_pressed() {
TreeItem *sel = tree->get_selected();
if (!sel)
- return; //?
+ return ;
- bool faved = sel->is_checked(0);
- String path = sel->get_metadata(0);
- if (faved)
- favorites.insert(path);
- else
- favorites.erase(path);
+ if (!sel->get_prev())
+ return;
+
+ String sw = sel->get_prev()->get_metadata(0);
+ String txt = sel->get_metadata(0);
+
+ Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs();
+
+ int a_idx=favorited.find(sw);
+ int b_idx=favorited.find(txt);
- timer->start();
+ if (a_idx==-1 || b_idx==-1)
+ return;
+ SWAP(favorited[a_idx],favorited[b_idx]);
+
+ EditorSettings::get_singleton()->set_favorite_dirs(favorited);
+
+ _update_tree();
+
+ if (!tree->get_root() || !tree->get_root()->get_children() || !tree->get_root()->get_children()->get_children())
+ return;
+ sel = tree->get_root()->get_children()->get_children();
+ while(sel) {
+ String t = sel->get_metadata(0);
+ if (t==txt) {
+ sel->select(0);
+ return;
+ }
+ sel=sel->get_next();
+ }
}
-void ScenesDock::_favorites_toggled(bool p_toggled) {
+void ScenesDock::_fav_down_pressed() {
+
+ TreeItem *sel = tree->get_selected();
+ if (!sel)
+ return ;
+
+ if (!sel->get_next())
+ return;
+
+ String sw = sel->get_next()->get_metadata(0);
+ String txt = sel->get_metadata(0);
+
+ Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs();
+
+ int a_idx=favorited.find(sw);
+ int b_idx=favorited.find(txt);
+
+ if (a_idx==-1 || b_idx==-1)
+ return;
+ SWAP(favorited[a_idx],favorited[b_idx]);
+
+ EditorSettings::get_singleton()->set_favorite_dirs(favorited);
_update_tree();
+
+ if (!tree->get_root() || !tree->get_root()->get_children() || !tree->get_root()->get_children()->get_children())
+ return;
+ sel = tree->get_root()->get_children()->get_children();
+ while(sel) {
+
+ String t = sel->get_metadata(0);
+ if (t==txt) {
+ sel->select(0);
+ return;
+ }
+ sel=sel->get_next();
+ }
+}
+
+void ScenesDock::_favorites_pressed() {
+
+ TreeItem *sel = tree->get_selected();
+ if (!sel)
+ return ;
+ String dir = sel->get_metadata(0);
+
+ int idx = -1;
+ Vector<String> favorites = EditorSettings::get_singleton()->get_favorite_dirs();
+ for(int i=0;i<favorites.size();i++) {
+
+ if (favorites[i]==dir) {
+ idx=i;
+ break;
+ }
+ }
+
+ if (button_favorite->is_pressed() && idx==-1) {
+ favorites.push_back(dir);
+ EditorSettings::get_singleton()->set_favorite_dirs(favorites);
+ _update_tree();
+ } else if (!button_favorite->is_pressed() && idx!=-1) {
+ favorites.remove(idx);
+ EditorSettings::get_singleton()->set_favorite_dirs(favorites);
+ _update_tree();
+ }
+
}
String ScenesDock::get_selected_path() const {
@@ -192,72 +352,739 @@ String ScenesDock::get_selected_path() const {
void ScenesDock::_instance_pressed() {
- TreeItem *sel = tree->get_selected();
- if (!sel)
- return;
- String path = sel->get_metadata(0);
+ if (tree_mode)
+ {
+ TreeItem *sel = tree->get_selected();
+ if (!sel)
+ return;
+ String path = sel->get_metadata(0);
+ }
+ else
+ {
+ int idx = -1;
+ for (int i = 0; i<files->get_item_count(); i++) {
+ if (files->is_selected(i))
+ {
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx<0)
+ return;
+
+ path = files->get_item_metadata(idx);
+ }
+
emit_signal("instance",path);
}
-void ScenesDock::_open_pressed(){
+void ScenesDock::_thumbnail_done(const String& p_path,const Ref<Texture>& p_preview, const Variant& p_udata) {
- TreeItem *sel = tree->get_selected();
- if (!sel) {
+ if (p_preview.is_valid() && path==p_path.get_base_dir()) {
+
+ int idx=p_udata;
+ if (idx>=files->get_item_count())
+ return;
+ String fpath = files->get_item_metadata(idx);
+ if (fpath!=p_path)
+ return;
+ files->set_item_icon(idx,p_preview);
+
+ }
+
+}
+
+void ScenesDock::_change_file_display() {
+
+ if (display_mode->is_pressed()) {
+ display_mode->set_icon( get_icon("FileThumbnail","EditorIcons"));
+
+ } else {
+ display_mode->set_icon( get_icon("FileList","EditorIcons"));
+ }
+
+ _update_files(true);
+}
+
+void ScenesDock::_update_files(bool p_keep_selection) {
+
+ Set<String> cselection;
+
+ if (p_keep_selection) {
+
+ for(int i=0;i<files->get_item_count();i++) {
+
+ if (files->is_selected(i))
+ cselection.insert(files->get_item_text(i));
+ }
+ }
+
+ files->clear();
+
+ EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->get_path(path);
+ if (!efd)
return;
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+ Ref<Texture> folder_thumbnail;
+ Ref<Texture> file_thumbnail;
+
+ bool use_thumbnails=!display_mode->is_pressed();
+
+ if (use_thumbnails) { //thumbnails
+
+ files->set_max_columns(0);
+ files->set_icon_mode(ItemList::ICON_MODE_TOP);
+ files->set_fixed_column_width(thumbnail_size*3/2);
+ files->set_max_text_lines(2);
+ files->set_min_icon_size(Size2(thumbnail_size,thumbnail_size));
+
+ if (!has_icon("ResizedFolder","EditorIcons")) {
+ Ref<ImageTexture> folder = get_icon("FolderBig","EditorIcons");
+ Image img = folder->get_data();
+ img.resize(thumbnail_size,thumbnail_size);
+ Ref<ImageTexture> resized_folder = Ref<ImageTexture>( memnew( ImageTexture));
+ resized_folder->create_from_image(img,0);
+ Theme::get_default()->set_icon("ResizedFolder","EditorIcons",resized_folder);
+ }
+
+ folder_thumbnail = get_icon("ResizedFolder","EditorIcons");
+
+ if (!has_icon("ResizedFile","EditorIcons")) {
+ Ref<ImageTexture> file = get_icon("FileBig","EditorIcons");
+ Image img = file->get_data();
+ img.resize(thumbnail_size,thumbnail_size);
+ Ref<ImageTexture> resized_file = Ref<ImageTexture>( memnew( ImageTexture));
+ resized_file->create_from_image(img,0);
+ Theme::get_default()->set_icon("ResizedFile","EditorIcons",resized_file);
+ }
+
+ file_thumbnail = get_icon("ResizedFile","EditorIcons");
+
+ } else {
+
+ files->set_icon_mode(ItemList::ICON_MODE_LEFT);
+ files->set_max_columns(1);
+ files->set_max_text_lines(1);
+ files->set_fixed_column_width(0);
+ files->set_min_icon_size(Size2());
+
}
- String path = sel->get_metadata(0);
- if (ResourceLoader::get_resource_type(path)=="PackedScene") {
- editor->open_request(path);
+ if (path!="res://") {
+
+ if (use_thumbnails) {
+ files->add_item("..",folder_thumbnail,true);
+ } else {
+ files->add_item("..",get_icon("folder","FileDialog"),true);
+ }
+
+ String bd = path.get_base_dir();
+ if (bd!="res://" && !bd.ends_with("/"))
+ bd+="/";
+
+ files->set_item_metadata(files->get_item_count()-1,bd);
+ }
+
+ for(int i=0;i<efd->get_subdir_count();i++) {
+
+ String dname=efd->get_subdir(i)->get_name();
+
+
+ if (use_thumbnails) {
+ files->add_item(dname,folder_thumbnail,true);
+ } else {
+ files->add_item(dname,get_icon("folder","FileDialog"),true);
+ }
+
+ files->set_item_metadata(files->get_item_count()-1,path.plus_file(dname)+"/");
+
+ if (cselection.has(dname))
+ files->select(files->get_item_count()-1,false);
+ }
+
+ for(int i=0;i<efd->get_file_count();i++) {
+
+ String fname=efd->get_file(i);
+ String fp = path.plus_file(fname);
+
+
+ String type = efd->get_file_type(i);
+ Ref<Texture> type_icon;
+
+ if (has_icon(type,"EditorIcons")) {
+ type_icon=get_icon(type,"EditorIcons");
+ } else {
+ type_icon=get_icon("Object","EditorIcons");
+ }
+
+ if (use_thumbnails) {
+ files->add_item(fname,file_thumbnail,true);
+ files->set_item_metadata(files->get_item_count()-1,fp);
+ files->set_item_tag_icon(files->get_item_count()-1,type_icon);
+ EditorResourcePreview::get_singleton()->queue_resource_preview(fp,this,"_thumbnail_done",files->get_item_count()-1);
+ } else {
+ files->add_item(fname,type_icon,true);
+ files->set_item_metadata(files->get_item_count()-1,fp);
+
+ }
+
+ if (cselection.has(fname))
+ files->select(files->get_item_count()-1,false);
+
+ }
+
+
+}
+
+void ScenesDock::_select_file(int p_idx) {
+
+ files->select(p_idx,true);
+ _open_pressed();
+}
+
+void ScenesDock::_go_to_tree() {
+
+ tree->show();
+ files->hide();
+ path_hb->hide();
+ _update_tree();
+ tree->grab_focus();
+ tree->ensure_cursor_is_visible();
+ button_favorite->show();
+ button_fav_up->show();
+ button_fav_down->show();
+ button_open->hide();
+ button_instance->hide();
+ button_open->hide();
+ file_options->hide();
+ tree_mode=true;
+}
+
+void ScenesDock::_go_to_dir(const String& p_dir){
+
+ DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ if (da->change_dir(p_dir)==OK) {
+ path=da->get_current_dir();
+ _update_files(false);
+ }
+ current_path->set_text(path);
+ memdelete(da);
+
+
+}
+void ScenesDock::_fs_changed() {
+
+ button_hist_prev->set_disabled(history_pos==0);
+ button_hist_next->set_disabled(history_pos+1==history.size());
+ scanning_vb->hide();
+
+ if (tree_mode) {
+
+ tree->show();
+ button_favorite->show();
+ button_fav_up->show();
+ button_fav_down->show();
+ _update_tree();
+ } else {
+ files->show();
+ path_hb->show();
+ button_instance->show();
+ button_open->show();
+ file_options->show();
+ _update_files(true);
+ }
+
+ set_process(false);
+}
+
+void ScenesDock::_set_scannig_mode() {
+
+ tree->hide();
+ button_favorite->hide();
+ button_fav_up->hide();
+ button_fav_down->hide();
+ button_instance->hide();
+ button_open->hide();
+ file_options->hide();
+ button_hist_prev->set_disabled(true);
+ button_hist_next->set_disabled(true);
+ scanning_vb->show();
+ path_hb->hide();
+ files->hide();
+ set_process(true);
+ if (EditorFileSystem::get_singleton()->is_scanning()) {
+ scanning_progress->set_val(EditorFileSystem::get_singleton()->get_scanning_progress()*100);
+ } else {
+ scanning_progress->set_val(0);
+ }
+
+}
+
+void ScenesDock::_fw_history() {
+
+ if (history_pos<history.size()-1)
+ history_pos++;
+
+ path=history[history_pos];
+
+ if (tree_mode) {
+ _update_tree();
+ tree->grab_focus();
+ tree->ensure_cursor_is_visible();
} else {
+ _update_files(false);
+ current_path->set_text(path);
+ }
- /*
+ button_hist_prev->set_disabled(history_pos==0);
+ button_hist_next->set_disabled(history_pos+1==history.size());
- RES res = ResourceLoader::load(path);
- if (res.is_null()) {
+}
+
+void ScenesDock::_bw_history() {
+
+ if (history_pos>0)
+ history_pos--;
+ path=history[history_pos];
+
+ if (tree_mode) {
+ _update_tree();
+ tree->grab_focus();
+ tree->ensure_cursor_is_visible();
+ } else {
+ _update_files(false);
+ current_path->set_text(path);
+ }
+ button_hist_prev->set_disabled(history_pos==0);
+ button_hist_next->set_disabled(history_pos+1==history.size());
+
+}
+
+void ScenesDock::_push_to_history() {
+
+ history.resize(history_pos+1);
+ if (history[history_pos]!=path) {
+ history.push_back(path);
+ history_pos++;
+ }
+
+ button_hist_prev->set_disabled(history_pos==0);
+ button_hist_next->set_disabled(history_pos+1==history.size());
+
+}
+
+
+void ScenesDock::_find_inside_move_files(EditorFileSystemDirectory *efsd,Vector<String>& files) {
+
+ for(int i=0;i<efsd->get_subdir_count();i++) {
+ _find_inside_move_files(efsd->get_subdir(i),files);
+ }
+ for(int i=0;i<efsd->get_file_count();i++) {
+ files.push_back(efsd->get_file_path(i));
+ }
+
+}
+
+void ScenesDock::_find_remaps(EditorFileSystemDirectory *efsd,Map<String,String> &renames,List<String>& to_remaps) {
+
+ for(int i=0;i<efsd->get_subdir_count();i++) {
+ _find_remaps(efsd->get_subdir(i),renames,to_remaps);
+ }
+ for(int i=0;i<efsd->get_file_count();i++) {
+ Vector<String> deps=efsd->get_file_deps(i);
+ for(int j=0;j<deps.size();j++) {
+ if (renames.has(deps[j])) {
+ to_remaps.push_back(efsd->get_file_path(i));
+ break;
+ }
+ }
+ }
+}
+
+
+void ScenesDock::_rename_operation(const String& p_to_path) {
+
+ if (move_files[0]==p_to_path) {
+ EditorNode::get_singleton()->show_warning("Same source and destination files, doing nothing.");
+ return;
+ }
+ if (FileAccess::exists(p_to_path)) {
+ EditorNode::get_singleton()->show_warning("Target file exists, can't overwrite. Delete first.");
+ return;
+ }
+
+ Map<String,String> renames;
+ renames[move_files[0]]=p_to_path;
+
+ List<String> remap;
+
+ _find_remaps(EditorFileSystem::get_singleton()->get_filesystem(),renames,remap);
+ print_line("found files to remap: "+itos(remap.size()));
+
+ //perform remaps
+ for(List<String>::Element *E=remap.front();E;E=E->next()) {
+
+ Error err = ResourceLoader::rename_dependencies(E->get(),renames);
+ print_line("remapping: "+E->get());
+
+ if (err!=OK) {
+ EditorNode::get_singleton()->add_io_error("Can't rename deps for:\n"+E->get()+"\n");
+ }
+ }
+
+ //finally, perform moves
+
+ DirAccess *da=DirAccess::create(DirAccess::ACCESS_RESOURCES);
+
+ Error err = da->rename(move_files[0],p_to_path);
+ print_line("moving file "+move_files[0]+" to "+p_to_path);
+ if (err!=OK) {
+ EditorNode::get_singleton()->add_io_error("Error moving file:\n"+move_files[0]+"\n");
+ }
+
+ //rescan everything
+ memdelete(da);
+ print_line("call rescan!");
+ _rescan();
+}
+
+
+void ScenesDock::_move_operation(const String& p_to_path) {
+
+ if (p_to_path==path) {
+ EditorNode::get_singleton()->show_warning("Same source and destination paths, doing nothing.");
+ return;
+ }
+
+ //find files inside dirs to be moved
+
+ Vector<String> inside_files;
+
+ for(int i=0;i<move_dirs.size();i++) {
+ if (p_to_path.begins_with(move_dirs[i])) {
+ EditorNode::get_singleton()->show_warning("Can't move directories to within themselves");
return;
- }*/
+ }
- editor->load_resource(path);
+ EditorFileSystemDirectory *efsd=EditorFileSystem::get_singleton()->get_path(move_dirs[i]);
+ if (!efsd)
+ continue;
+ _find_inside_move_files(efsd,inside_files);
}
-// emit_signal("open",path);
+ //make list of remaps
+ Map<String,String> renames;
+ String repfrom=path=="res://"?path:String(path+"/");
+ String repto=p_to_path=="res://"?p_to_path:String(p_to_path+"/");
+
+ for(int i=0;i<move_files.size();i++) {
+ renames[move_files[i]]=move_files[i].replace_first(repfrom,repto);
+ print_line("move file "+move_files[i]+" -> "+renames[move_files[i]]);
+ }
+ for(int i=0;i<inside_files.size();i++) {
+ renames[inside_files[i]]=inside_files[i].replace_first(repfrom,repto);
+ print_line("inside file "+inside_files[i]+" -> "+renames[inside_files[i]]);
+ }
+
+ //make list of files that will be run the remapping
+ List<String> remap;
+
+ _find_remaps(EditorFileSystem::get_singleton()->get_filesystem(),renames,remap);
+ print_line("found files to remap: "+itos(remap.size()));
+
+ //perform remaps
+ for(List<String>::Element *E=remap.front();E;E=E->next()) {
+
+ Error err = ResourceLoader::rename_dependencies(E->get(),renames);
+ print_line("remapping: "+E->get());
+
+ if (err!=OK) {
+ EditorNode::get_singleton()->add_io_error("Can't rename deps for:\n"+E->get()+"\n");
+ }
+ }
+
+ //finally, perform moves
+
+ DirAccess *da=DirAccess::create(DirAccess::ACCESS_RESOURCES);
+
+ for(int i=0;i<move_files.size();i++) {
+
+ String to = move_files[i].replace_first(repfrom,repto);
+ Error err = da->rename(move_files[i],to);
+ print_line("moving file "+move_files[i]+" to "+to);
+ if (err!=OK) {
+ EditorNode::get_singleton()->add_io_error("Error moving file:\n"+move_files[i]+"\n");
+ }
+ }
+
+ for(int i=0;i<move_dirs.size();i++) {
+
+ String to = p_to_path.plus_file(move_dirs[i].get_file());
+ Error err = da->rename(move_dirs[i],to);
+ print_line("moving dir "+move_dirs[i]+" to "+to);
+ if (err!=OK) {
+ EditorNode::get_singleton()->add_io_error("Error moving dir:\n"+move_dirs[i]+"\n");
+ }
+ }
+
+ memdelete(da);
+ //rescan everything
+ print_line("call rescan!");
+ _rescan();
}
-void ScenesDock::_save_favorites() {
+void ScenesDock::_file_option(int p_option) {
+
+ switch(p_option) {
+
+ case FILE_DEPENDENCIES: {
+
+ int idx = files->get_current();
+ if (idx<0 || idx>=files->get_item_count())
+ break;
+ String path = files->get_item_metadata(idx);
+ deps_editor->edit(path);
+ } break;
+ case FILE_OWNERS: {
+
+ int idx = files->get_current();
+ if (idx<0 || idx>=files->get_item_count())
+ break;
+ String path = files->get_item_metadata(idx);
+ owners_editor->show(path);
+ } break;
+ case FILE_MOVE: {
+
+ move_dirs.clear();;
+ move_files.clear();
+
+ for(int i=0;i<files->get_item_count();i++) {
+
+ String path = files->get_item_metadata(i);
+ if (!files->is_selected(i))
+ continue;
+
+ if (files->get_item_text(i)=="..") {
+ EditorNode::get_singleton()->show_warning("Can't operate on '..'");
+ return;
+ }
+
+ if (path.ends_with("/")) {
+ move_dirs.push_back(path.substr(0,path.length()-1));
+ } else {
+ move_files.push_back(path);
+ }
+ }
+
+
+ if (move_dirs.empty() && move_files.size()==1) {
+
+ rename_dialog->clear_filters();
+ rename_dialog->add_filter("*."+move_files[0].extension());
+ rename_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
+ rename_dialog->set_current_path(move_files[0]);
+ rename_dialog->popup_centered_ratio();
+ rename_dialog->set_title("Pick New Name and Location For: "+move_files[0].get_file());
+
+
+ } else {
+ //just move
+ move_dialog->popup_centered_ratio();
+ }
+
+
+ } break;
+ case FILE_REMOVE: {
- String path = Globals::get_singleton()->get_resource_path()+"/favorites.cfg";
- FileAccess *f=FileAccess::open(path,FileAccess::WRITE);
- ERR_FAIL_COND(!f);
- for(Set<String>::Element *E=favorites.front();E;E=E->next() ) {
+ Vector<String> torem;
+
+ for(int i=0;i<files->get_item_count();i++) {
+
+ String path = files->get_item_metadata(i);
+ if (path.ends_with("/") || !files->is_selected(i))
+ continue;
+ torem.push_back(path);
+ }
+
+ if (torem.empty()) {
+ EditorNode::get_singleton()->show_warning("No files selected!");
+ break;
+ }
+
+ remove_dialog->show(torem);
+ //1) find if used
+ //2) warn
+
+ } break;
+ case FILE_INFO: {
+
+ } break;
- CharString utf8f = E->get().utf8();
- f->store_buffer((const uint8_t*)utf8f.get_data(),utf8f.length());
- f->store_8('\n');
}
+}
+
+void ScenesDock::_open_pressed(){
+
+
+ if (tree_mode) {
+
+ TreeItem *sel = tree->get_selected();
+ if (!sel) {
+ return;
+ }
+ path = sel->get_metadata(0);
+ /*if (path!="res://" && path.ends_with("/")) {
+ path=path.substr(0,path.length()-1);
+ }*/
+
+ tree_mode=false;
+
+ tree->hide();
+ files->show();
+ path_hb->show();
+ button_favorite->hide();
+ button_fav_up->hide();
+ button_fav_down->hide();
+ button_instance->show();
+ button_open->show();
+ file_options->show();
+
+ _update_files(false);
+
+ current_path->set_text(path);
+
+ _push_to_history();
+
+
+ } else {
+
+ int idx=-1;
+ for(int i=0;i<files->get_item_count();i++) {
+ if (files->is_selected(i)) {
+ idx=i;
+ break;
+ }
+ }
+
+ if (idx<0)
+ return;
+
+
+
+ String path = files->get_item_metadata(idx);
+
+ if (path.ends_with("/")) {
+ if (path!="res://") {
+ path=path.substr(0,path.length()-1);
+ }
+ this->path=path;
+ _update_files(false);
+ current_path->set_text(path);
+ _push_to_history();
+ } else {
+
+ if (ResourceLoader::get_resource_type(path)=="PackedScene") {
+
+ editor->open_request(path);
+ } else {
+
+ editor->load_resource(path);
+ }
+ }
+ }
+
+// emit_signal("open",path);
- f->close();
- memdelete(f);
}
+
void ScenesDock::_rescan() {
+ _set_scannig_mode();
EditorFileSystem::get_singleton()->scan();
+
+}
+
+void ScenesDock::fix_dependencies(const String& p_for_file) {
+ deps_editor->edit(p_for_file);
+}
+
+void ScenesDock::open(const String& p_path) {
+
+
+ String npath;
+ String nfile;
+
+ if (p_path.ends_with("/")) {
+
+ if (p_path!="res://")
+ npath=p_path.substr(0,p_path.length()-1);
+ else
+ npath="res://";
+ } else {
+ nfile=p_path.get_file();
+ npath=p_path.get_base_dir();
+ }
+
+ path=npath;
+
+ if (tree_mode && nfile=="") {
+ _update_tree();
+ tree->grab_focus();
+ tree->call_deferred("ensure_cursor_is_visible");
+ _push_to_history();
+ return;
+ } else if (tree_mode){
+ _update_tree();
+ tree->grab_focus();
+ tree->ensure_cursor_is_visible();
+ _open_pressed();
+ current_path->set_text(path);
+ } else {
+ _update_files(false);
+ _push_to_history();
+ }
+
+ for(int i=0;i<files->get_item_count();i++) {
+
+ String md = files->get_item_metadata(i);
+ if (md==p_path) {
+ files->select(i,true);
+ files->ensure_current_is_visible();
+ break;
+ }
+ }
+
}
void ScenesDock::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_update_tree"),&ScenesDock::_update_tree);
ObjectTypeDB::bind_method(_MD("_rescan"),&ScenesDock::_rescan);
- ObjectTypeDB::bind_method(_MD("_favorites_toggled"),&ScenesDock::_favorites_toggled);
- ObjectTypeDB::bind_method(_MD("_favorite_toggled"),&ScenesDock::_favorite_toggled);
+ ObjectTypeDB::bind_method(_MD("_favorites_pressed"),&ScenesDock::_favorites_pressed);
ObjectTypeDB::bind_method(_MD("_instance_pressed"),&ScenesDock::_instance_pressed);
ObjectTypeDB::bind_method(_MD("_open_pressed"),&ScenesDock::_open_pressed);
- ObjectTypeDB::bind_method(_MD("_save_favorites"),&ScenesDock::_save_favorites);
+
+ ObjectTypeDB::bind_method(_MD("_thumbnail_done"),&ScenesDock::_thumbnail_done);
+ ObjectTypeDB::bind_method(_MD("_select_file"), &ScenesDock::_select_file);
+ ObjectTypeDB::bind_method(_MD("_go_to_tree"), &ScenesDock::_go_to_tree);
+ ObjectTypeDB::bind_method(_MD("_go_to_dir"), &ScenesDock::_go_to_dir);
+ ObjectTypeDB::bind_method(_MD("_change_file_display"), &ScenesDock::_change_file_display);
+ ObjectTypeDB::bind_method(_MD("_fw_history"), &ScenesDock::_fw_history);
+ ObjectTypeDB::bind_method(_MD("_bw_history"), &ScenesDock::_bw_history);
+ ObjectTypeDB::bind_method(_MD("_fs_changed"), &ScenesDock::_fs_changed);
+ ObjectTypeDB::bind_method(_MD("_dir_selected"), &ScenesDock::_dir_selected);
+ ObjectTypeDB::bind_method(_MD("_fav_up_pressed"), &ScenesDock::_fav_up_pressed);
+ ObjectTypeDB::bind_method(_MD("_fav_down_pressed"), &ScenesDock::_fav_down_pressed);
+ ObjectTypeDB::bind_method(_MD("_file_option"), &ScenesDock::_file_option);
+ ObjectTypeDB::bind_method(_MD("_move_operation"), &ScenesDock::_move_operation);
+ ObjectTypeDB::bind_method(_MD("_rename_operation"), &ScenesDock::_rename_operation);
ADD_SIGNAL(MethodInfo("instance"));
ADD_SIGNAL(MethodInfo("open"));
@@ -271,153 +1098,156 @@ ScenesDock::ScenesDock(EditorNode *p_editor) {
HBoxContainer *toolbar_hbc = memnew( HBoxContainer );
add_child(toolbar_hbc);
+ button_hist_prev = memnew( ToolButton );
+ toolbar_hbc->add_child(button_hist_prev);
+ button_hist_prev->set_disabled(true);
+ button_hist_prev->set_tooltip("Previous Directory");
+
+ button_hist_next = memnew( ToolButton );
+ toolbar_hbc->add_child(button_hist_next);
+ button_hist_next->set_disabled(true);
+ button_hist_prev->set_focus_mode(FOCUS_NONE);
+ button_hist_next->set_focus_mode(FOCUS_NONE);
+ button_hist_next->set_tooltip("Next Directory");
+
button_reload = memnew( Button );
button_reload->set_flat(true);
button_reload->connect("pressed",this,"_rescan");
toolbar_hbc->add_child(button_reload);
+ button_reload->set_focus_mode(FOCUS_NONE);
+ button_reload->set_tooltip("Re-Scan Filesystem");
+
+ toolbar_hbc->add_spacer();
+
+ button_fav_up = memnew( ToolButton );
+ button_fav_up->set_flat(true);
+ toolbar_hbc->add_child(button_fav_up);
+ button_fav_up->set_disabled(true);
+ button_fav_up->connect("pressed",this,"_fav_up_pressed");
+ button_fav_up->set_tooltip("Move Favorite Up");
+
+ button_fav_down = memnew( ToolButton );
+ button_fav_down->set_flat(true);
+ toolbar_hbc->add_child(button_fav_down);
+ button_fav_down->set_disabled(true);
+ button_fav_down->connect("pressed",this,"_fav_down_pressed");
+ button_fav_down->set_tooltip("Move Favorite Down");
button_favorite = memnew( Button );
button_favorite->set_flat(true);
button_favorite->set_toggle_mode(true);
- button_favorite->connect("toggled",this,"_favorites_toggled");
+ button_favorite->connect("pressed",this,"_favorites_pressed");
toolbar_hbc->add_child(button_favorite);
+ button_favorite->set_tooltip("Toggle folder status as Favorite");
+
+ button_favorite->set_focus_mode(FOCUS_NONE);
+ button_fav_up->set_focus_mode(FOCUS_NONE);
+ button_fav_down->set_focus_mode(FOCUS_NONE);
- toolbar_hbc->add_spacer();
button_open = memnew( Button );
button_open->set_flat(true);
button_open->connect("pressed",this,"_open_pressed");
toolbar_hbc->add_child(button_open);
+ button_open->hide();
+ button_open->set_focus_mode(FOCUS_NONE);
+ button_open->set_tooltip("Open the selected file.\nOpen as scene if a scene, or as resource otherwise.");
+
button_instance = memnew( Button );
button_instance->set_flat(true);
button_instance->connect("pressed",this,"_instance_pressed");
toolbar_hbc->add_child(button_instance);
+ button_instance->hide();
+ button_instance->set_focus_mode(FOCUS_NONE);
+ button_instance->set_tooltip("Instance the selected scene(s) as child of the selected node.");
+
+
+ file_options = memnew( MenuButton );
+ toolbar_hbc->add_child(file_options);
+ file_options->get_popup()->add_item("Rename or Move",FILE_MOVE);
+ file_options->get_popup()->add_item("Delete",FILE_REMOVE);
+ file_options->get_popup()->add_separator();
+ file_options->get_popup()->add_item("Edit Dependencies",FILE_DEPENDENCIES);
+ file_options->get_popup()->add_item("View Owners",FILE_OWNERS);
+ //file_options->get_popup()->add_item("Info",FILE_INFO);
+ file_options->hide();
+ file_options->set_focus_mode(FOCUS_NONE);
+ file_options->set_tooltip("Miscenaneous options related to resources on disk.");
tree = memnew( Tree );
- tree_filter=memnew( ScenesDockFilter() );
- tree_filter->connect("filter_changed", this, "_update_tree");
- add_child(tree_filter);
+
+ tree->set_hide_root(true);
add_child(tree);
+
tree->set_v_size_flags(SIZE_EXPAND_FILL);
tree->connect("item_edited",this,"_favorite_toggled");
tree->connect("item_activated",this,"_open_pressed");
-
- timer = memnew( Timer );
- timer->set_one_shot(true);
- timer->set_wait_time(2);
- timer->connect("timeout",this,"_save_favorites");
- add_child(timer);
-
+ tree->connect("cell_selected",this,"_dir_selected");
+
+ files = memnew( ItemList );
+ files->set_v_size_flags(SIZE_EXPAND_FILL);
+ files->set_select_mode(ItemList::SELECT_MULTI);
+
+ path_hb = memnew( HBoxContainer );
+ button_back = memnew( ToolButton );
+ path_hb->add_child(button_back);
+ current_path=memnew( LineEdit );
+ current_path->set_h_size_flags(SIZE_EXPAND_FILL);
+ path_hb->add_child(current_path);
+ display_mode = memnew( ToolButton );
+ path_hb->add_child(display_mode);
+ display_mode->set_toggle_mode(true);
+ add_child(path_hb);
+ path_hb->hide();
+
+
+ add_child(files);
+ files->hide();
+
+ scanning_vb = memnew( VBoxContainer );
+ Label *slabel = memnew( Label );
+ slabel->set_text("Scanning Files,\nPlease Wait..");
+ slabel->set_align(Label::ALIGN_CENTER);
+ scanning_vb->add_child(slabel);
+ scanning_progress = memnew( ProgressBar );
+ scanning_vb->add_child(scanning_progress);
+ add_child(scanning_vb);
+ scanning_vb->hide();
+
+
+
+ deps_editor = memnew( DependencyEditor );
+ add_child(deps_editor);
+
+ owners_editor = memnew( DependencyEditorOwners);
+ add_child(owners_editor);
+
+ remove_dialog = memnew( DependencyRemoveDialog);
+ add_child(remove_dialog);
+
+ move_dialog = memnew( EditorDirDialog );
+ add_child(move_dialog);
+ move_dialog->connect("dir_selected",this,"_move_operation");
+ move_dialog->get_ok()->set_text("Move");
+
+ rename_dialog = memnew( EditorFileDialog );
+ rename_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
+ rename_dialog->connect("file_selected",this,"_rename_operation");
+ add_child(rename_dialog);
updating_tree=false;
+ initialized=false;
-}
-
-ScenesDock::~ScenesDock() {
-
-}
-
-void ScenesDockFilter::_setup_filters() {
-
- filter_option->clear();
- filter_option->add_item("Path");
- filter_option->add_item("Name");
- filter_option->add_item("Folder");
-#if 0
- List<String> extensions;
- ResourceLoader::get_recognized_extensions_for_type("",&extensions);
-
- file_filter->add_item("All Files (*)");
- filters.push_back("*");
-
- List<String> filter_texts;
- for(int i=0;i<extensions.size();i++) {
- filter_texts.push_back("*."+extensions[i]+" ; "+extensions[i].to_upper());
- filters.push_back(extensions[i]);
- }
- for(int i=0;i<filter_texts.size();i++) {
-
- String flt=filter_texts[i].get_slice(";",0).strip_edges();
- String desc=filter_texts[i].get_slice(";",1).strip_edges();
- if (desc.length())
- file_filter->add_item(desc+" ( "+flt+" )");
- else
- file_filter->add_item("( "+flt+" )");
- }
-#endif
-}
-
-void ScenesDockFilter::_command(int p_command) {
- switch (p_command) {
-
- case CMD_CLEAR_FILTER: {
- if (search_box->get_text()!="") {
- search_box->clear();
- emit_signal("filter_changed");
- }
- }break;
- }
-}
+ history.push_back("res://");
+ history_pos=0;
+ tree_mode=true;
-void ScenesDockFilter::_search_text_changed(const String &p_newtext) {
- emit_signal("filter_changed");
-}
-String ScenesDockFilter::get_search_term() {
- return search_box->get_text().strip_edges();
}
-ScenesDockFilter::FilterOption ScenesDockFilter::get_file_filter() {
- return _current_filter;
-}
-
-void ScenesDockFilter::_file_filter_selected(int p_idx) {
- FilterOption selected = (FilterOption)(filter_option->get_selected());
- if (_current_filter != selected ) {
- _current_filter = selected;
- emit_signal("filter_changed");
- }
-}
-
-void ScenesDockFilter::_notification(int p_what) {
- switch(p_what) {
- case NOTIFICATION_ENTER_TREE: {
- clear_search_button->set_icon(get_icon("CloseHover","EditorIcons"));
- } break;
- }
-}
-
-void ScenesDockFilter::_bind_methods() {
-
- ObjectTypeDB::bind_method(_MD("_command"),&ScenesDockFilter::_command);
- ObjectTypeDB::bind_method(_MD("_search_text_changed"), &ScenesDockFilter::_search_text_changed);
- ObjectTypeDB::bind_method(_MD("_file_filter_selected"), &ScenesDockFilter::_file_filter_selected);
-
- ADD_SIGNAL( MethodInfo("filter_changed") );
-}
-
-ScenesDockFilter::ScenesDockFilter() {
-
- _current_filter = FILTER_PATH;
-
- filter_option = memnew( OptionButton );
- filter_option->set_custom_minimum_size(Size2(60,10));
- filter_option->set_clip_text(true);
- filter_option->connect("item_selected", this, "_file_filter_selected");
- add_child(filter_option);
-
- _setup_filters();
-
- search_box = memnew( LineEdit );
- search_box->connect("text_changed",this,"_search_text_changed");
- search_box->set_h_size_flags(SIZE_EXPAND_FILL);
- add_child(search_box);
-
- clear_search_button = memnew( ToolButton );
- clear_search_button->connect("pressed",this,"_command",make_binds(CMD_CLEAR_FILTER));
- add_child(clear_search_button);
+ScenesDock::~ScenesDock() {
}
diff --git a/tools/editor/scenes_dock.h b/tools/editor/scenes_dock.h
index 9849f5ace0..d045124bf7 100644
--- a/tools/editor/scenes_dock.h
+++ b/tools/editor/scenes_dock.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -36,40 +36,112 @@
#include "scene/gui/tool_button.h"
#include "scene/gui/option_button.h"
#include "scene/gui/box_container.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/item_list.h"
+#include "scene/gui/progress_bar.h"
+
#include "os/dir_access.h"
#include "os/thread.h"
#include "editor_file_system.h"
-
+#include "editor_dir_dialog.h"
+#include "dependency_editor.h"
class EditorNode;
-class ScenesDockFilter;
+
class ScenesDock : public VBoxContainer {
OBJ_TYPE( ScenesDock, VBoxContainer );
+ enum FileMenu {
+ FILE_DEPENDENCIES,
+ FILE_OWNERS,
+ FILE_MOVE,
+ FILE_REMOVE,
+ FILE_REIMPORT,
+ FILE_INFO
+ };
+
+
+ VBoxContainer *scanning_vb;
+ ProgressBar *scanning_progress;
+
EditorNode *editor;
Set<String> favorites;
Button *button_reload;
Button *button_instance;
Button *button_favorite;
+ Button *button_fav_up;
+ Button *button_fav_down;
Button *button_open;
- Timer *timer;
+ Button *button_back;
+ Button *display_mode;
+ Button *button_hist_next;
+ Button *button_hist_prev;
+ LineEdit *current_path;
+ HBoxContainer *path_hb;
+
+ MenuButton *file_options;
+
+
+ DependencyEditor *deps_editor;
+ DependencyEditorOwners *owners_editor;
+ DependencyRemoveDialog *remove_dialog;
+
+ EditorDirDialog *move_dialog;
+ EditorFileDialog *rename_dialog;
- ScenesDockFilter *tree_filter;
+ Vector<String> move_dirs;
+ Vector<String> move_files;
+
+
+ Vector<String> history;
+ int history_pos;
+
+ String path;
+
+ bool initialized;
bool updating_tree;
- Tree * tree;
+ Tree * tree; //directories
+ ItemList *files;
+
+ bool tree_mode;
+
+ void _go_to_tree();
+ void _go_to_dir(const String& p_dir);
+ void _select_file(int p_idx);
+
bool _create_tree(TreeItem *p_parent,EditorFileSystemDirectory *p_dir);
+ void _thumbnail_done(const String& p_path,const Ref<Texture>& p_preview, const Variant& p_udata);
+ void _find_inside_move_files(EditorFileSystemDirectory *efsd,Vector<String>& files);
+ void _find_remaps(EditorFileSystemDirectory *efsd,Map<String,String> &renames,List<String>& to_remaps);
+
+ void _rename_operation(const String& p_to_path);
+ void _move_operation(const String& p_to_path);
+
+
+ void _file_option(int p_option);
+ void _update_files(bool p_keep_selection);
+ void _change_file_display();
+ void _fs_changed();
+ void _fw_history();
+ void _bw_history();
+ void _push_to_history();
+
+ void _fav_up_pressed();
+ void _fav_down_pressed();
+ void _dir_selected();
void _update_tree();
void _rescan();
- void _favorites_toggled(bool);
- void _favorite_toggled();
+ void _set_scannig_mode();
+
+ void _favorites_pressed();
void _instance_pressed();
void _open_pressed();
- void _save_favorites();
+
protected:
void _notification(int p_what);
@@ -77,48 +149,14 @@ protected:
public:
String get_selected_path() const;
+ void open(const String& p_path);
+
+ void fix_dependencies(const String& p_for_file);
+
ScenesDock(EditorNode *p_editor);
~ScenesDock();
};
-class ScenesDockFilter : public HBoxContainer {
-
- OBJ_TYPE( ScenesDockFilter, HBoxContainer );
-
-private:
- friend class ScenesDock;
-
- enum Command {
- CMD_CLEAR_FILTER,
- };
-
- Tree *tree;
- OptionButton *filter_option;
- LineEdit *search_box;
- ToolButton *clear_search_button;
-
- enum FilterOption {
- FILTER_PATH, // NAME or Folder
- FILTER_NAME,
- FILTER_FOLDER,
- };
- FilterOption _current_filter;
- //Vector<String> filters;
-
- void _command(int p_command);
- void _search_text_changed(const String& p_newtext);
- void _setup_filters();
- void _file_filter_selected(int p_idx);
-
-protected:
- void _notification(int p_what);
- static void _bind_methods();
-
-public:
- String get_search_term();
- FilterOption get_file_filter();
- ScenesDockFilter();
-};
#endif // SCENES_DOCK_H
diff --git a/tools/editor/script_create_dialog.cpp b/tools/editor/script_create_dialog.cpp
index 89c0bd6ab4..622150ab68 100644
--- a/tools/editor/script_create_dialog.cpp
+++ b/tools/editor/script_create_dialog.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -74,17 +74,17 @@ bool ScriptCreateDialog::_validate(const String& p_string) {
void ScriptCreateDialog::_class_name_changed(const String& p_name) {
if (!_validate(parent_name->get_text())) {
- error_label->set_text("INVALID PARENT CLASS NAME");
+ error_label->set_text("Invaild parent class name");
error_label->add_color_override("font_color",Color(1,0.4,0.0,0.8));
} else if (class_name->is_editable()) {
if (class_name->get_text()=="") {
error_label->set_text("Valid Chars: a-z A-Z 0-9 _");
error_label->add_color_override("font_color",Color(1,1,1,0.6));
} else if (!_validate(class_name->get_text())) {
- error_label->set_text("INVALID CLASS NAME");
+ error_label->set_text("Invalid class name");
error_label->add_color_override("font_color",Color(1,0.2,0.2,0.8));
} else {
- error_label->set_text("Name is Valid");
+ error_label->set_text("Valid Name");
error_label->add_color_override("font_color",Color(0,1.0,0.8,0.8));
}
} else {
@@ -99,12 +99,12 @@ void ScriptCreateDialog::ok_pressed() {
if (class_name->is_editable() && !_validate(class_name->get_text())) {
alert->set_text("Class Name is Invalid!");
- alert->popup_centered(Size2(200,60));
+ alert->popup_centered_minsize();
return;
}
if (!_validate(parent_name->get_text())) {
alert->set_text("Parent Class Name is Invalid!");
- alert->popup_centered(Size2(200,60));
+ alert->popup_centered_minsize();
return;
@@ -134,7 +134,7 @@ void ScriptCreateDialog::ok_pressed() {
if (!path_valid) {
alert->set_text("Path is Invalid!");
- alert->popup_centered(Size2(200,60));
+ alert->popup_centered_minsize();
return;
}
@@ -142,7 +142,7 @@ void ScriptCreateDialog::ok_pressed() {
if (err!=OK) {
alert->set_text("Could not create script in filesystem: "+String(""));
- alert->popup_centered(Size2(200,60));
+ alert->popup_centered_minsize();
return;
}
scr->set_path(lpath);
@@ -184,7 +184,7 @@ void ScriptCreateDialog::_built_in_pressed() {
void ScriptCreateDialog::_browse_path() {
- file_browse->set_mode(FileDialog::MODE_SAVE_FILE);
+ file_browse->set_mode(EditorFileDialog::MODE_SAVE_FILE);
file_browse->clear_filters();
List<String> extensions;
@@ -363,9 +363,9 @@ ScriptCreateDialog::ScriptCreateDialog() {
set_size(Size2(200,150));
set_hide_on_ok(false);
- set_title("Create Script for Node..");;
+ set_title("Create Script for Node");
- file_browse = memnew( FileDialog );
+ file_browse = memnew( EditorFileDialog );
file_browse->connect("file_selected",this,"_file_selected");
add_child(file_browse);
get_ok()->set_text("Create");
diff --git a/tools/editor/script_create_dialog.h b/tools/editor/script_create_dialog.h
index 42fa9d68c3..59fde8fbd5 100644
--- a/tools/editor/script_create_dialog.h
+++ b/tools/editor/script_create_dialog.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,7 +32,7 @@
#include "scene/gui/dialogs.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/option_button.h"
-#include "scene/gui/file_dialog.h"
+#include "tools/editor/editor_file_dialog.h"
#include "scene/gui/check_button.h"
class ScriptCreateDialog : public ConfirmationDialog {
@@ -44,7 +44,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
LineEdit *parent_name;
OptionButton *language_menu;
LineEdit *file_path;
- FileDialog *file_browse;
+ EditorFileDialog *file_browse;
CheckButton *internal;
VBoxContainer *path_vb;
AcceptDialog *alert;
diff --git a/tools/editor/script_editor_debugger.cpp b/tools/editor/script_editor_debugger.cpp
index 024377ad18..60f2afa2c2 100644
--- a/tools/editor/script_editor_debugger.cpp
+++ b/tools/editor/script_editor_debugger.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -169,6 +169,17 @@ void ScriptEditorDebugger::_scene_tree_request() {
}
+void ScriptEditorDebugger::_video_mem_request() {
+
+ ERR_FAIL_COND(connection.is_null());
+ ERR_FAIL_COND(!connection->is_connected());
+
+ Array msg;
+ msg.push_back("request_video_mem");
+ ppeer->put_var(msg);
+
+}
+
Size2 ScriptEditorDebugger::get_minimum_size() const {
Size2 ms = Control::get_minimum_size();
@@ -241,6 +252,33 @@ void ScriptEditorDebugger::_parse_message(const String& p_msg,const Array& p_dat
lv[level]=it;
}
+ le_clear->set_disabled(false);
+ le_set->set_disabled(false);
+
+ } else if (p_msg=="message:video_mem") {
+
+ vmem_tree->clear();
+ TreeItem* root=vmem_tree->create_item();
+
+ int total=0;
+
+ for(int i=0;i<p_data.size();i+=4) {
+
+ TreeItem *it = vmem_tree->create_item(root);
+ String type=p_data[i+1];
+ int bytes=p_data[i+3].operator int();
+ it->set_text(0,p_data[i+0]); //path
+ it->set_text(1,type); //type
+ it->set_text(2,p_data[i+2]); //type
+ it->set_text(3,String::humanize_size(bytes)); //type
+ total+=bytes;
+
+ if (has_icon(type,"EditorIcons"))
+ it->set_icon(0,get_icon(type,"EditorIcons"));
+ }
+
+ vmem_total->set_tooltip("Bytes: "+itos(total));
+ vmem_total->set_text(String::humanize_size(total));
} else if (p_msg=="stack_dump") {
@@ -342,6 +380,63 @@ void ScriptEditorDebugger::_parse_message(const String& p_msg,const Array& p_dat
perf_history.push_front(p);
perf_draw->update();
+ } else if (p_msg=="error") {
+
+ Array err = p_data[0];
+
+ Array vals;
+ vals.push_back(err[0]);
+ vals.push_back(err[1]);
+ vals.push_back(err[2]);
+ vals.push_back(err[3]);
+
+ bool warning = err[9];
+ bool e;
+ String time = String("%d:%02d:%02d:%04d").sprintf(vals,&e);
+ String txt=time+" - "+String(err[8]);
+
+ String tooltip="Type:"+String(warning?"Warning":"Error");
+ tooltip+="\nDescription: "+String(err[8]);
+ tooltip+="\nTime: "+time;
+ tooltip+="\nC Error: "+String(err[7]);
+ tooltip+="\nC Source: "+String(err[5])+":"+String(err[6]);
+ tooltip+="\nC Function: "+String(err[4]);
+
+
+
+ error_list->add_item(txt,EditorNode::get_singleton()->get_gui_base()->get_icon(warning?"Warning":"Error","EditorIcons"));
+ error_list->set_item_tooltip( error_list->get_item_count() -1,tooltip );
+
+ int scc = p_data[1];
+
+ Array stack;
+ stack.resize(scc);
+ for(int i=0;i<scc;i++) {
+ stack[i]=p_data[2+i];
+ }
+
+ error_list->set_item_metadata( error_list->get_item_count() -1,stack );
+
+ error_count++;
+ /*
+ int count = p_data[1];
+
+ Array cstack;
+
+ OutputError oe = errors.front()->get();
+
+ packet_peer_stream->put_var(oe.hr);
+ packet_peer_stream->put_var(oe.min);
+ packet_peer_stream->put_var(oe.sec);
+ packet_peer_stream->put_var(oe.msec);
+ packet_peer_stream->put_var(oe.source_func);
+ packet_peer_stream->put_var(oe.source_file);
+ packet_peer_stream->put_var(oe.source_line);
+ packet_peer_stream->put_var(oe.error);
+ packet_peer_stream->put_var(oe.error_descr);
+ packet_peer_stream->put_var(oe.warning);
+ packet_peer_stream->put_var(oe.callstack);
+ */
} else if (p_msg=="kill_me") {
editor->call_deferred("stop_child_process");
@@ -443,10 +538,24 @@ void ScriptEditorDebugger::_notification(int p_what) {
tb->set_hover_texture( get_icon("CloseHover","EditorIcons"));
tb->set_pressed_texture( get_icon("Close","EditorIcons"));
scene_tree_refresh->set_icon( get_icon("Reload","EditorIcons"));
+ le_set->connect("pressed",this,"_live_edit_set");
+ le_clear->connect("pressed",this,"_live_edit_clear");
+ error_list->connect("item_selected",this,"_error_selected");
+ error_stack->connect("item_selected",this,"_error_stack_selected");
+ vmem_refresh->set_icon( get_icon("Reload","EditorIcons"));
} break;
case NOTIFICATION_PROCESS: {
+ if (error_count!=last_error_count) {
+
+ if (error_count==0) {
+ error_split->set_name("Errors");
+ } else {
+ error_split->set_name("Errors ("+itos(error_count)+")");
+ }
+ last_error_count=error_count;
+ }
if (connection.is_null()) {
if (server->is_connection_available()) {
@@ -468,6 +577,15 @@ void ScriptEditorDebugger::_notification(int p_what) {
emit_signal("show_debugger",true);
reason->set_text("Child Process Connected");
reason->set_tooltip("Child Process Connected");
+ scene_tree->clear();
+ le_set->set_disabled(true);
+ le_clear->set_disabled(false);
+ error_list->clear();
+ error_stack->clear();
+ error_count=0;
+ //live_edit_root->set_text("/root");
+
+ update_live_edit_root();
} else {
@@ -478,8 +596,6 @@ void ScriptEditorDebugger::_notification(int p_what) {
if (!connection->is_connected()) {
stop();
editor->notify_child_process_exited(); //somehow, exited
- msgdialog->set_text("Process being debugged exited.");
- msgdialog->popup_centered(Size2(250,100));
break;
};
@@ -615,6 +731,10 @@ void ScriptEditorDebugger::stop(){
log_forced_visible=false;
}
+ node_path_cache.clear();
+ res_path_cache.clear();
+ le_clear->set_disabled(false);
+ le_set->set_disabled(true);
hide();
@@ -666,6 +786,381 @@ String ScriptEditorDebugger::get_var_value(const String& p_var) const {
return variables->get_var_value(p_var);
}
+int ScriptEditorDebugger::_get_node_path_cache(const NodePath& p_path) {
+
+ const int *r = node_path_cache.getptr(p_path);
+ if (r)
+ return *r;
+
+ last_path_id++;
+
+ node_path_cache[p_path]=last_path_id;
+ Array msg;
+ msg.push_back("live_node_path");
+ msg.push_back(p_path);
+ msg.push_back(last_path_id);
+ ppeer->put_var(msg);
+
+
+ return last_path_id;
+}
+
+int ScriptEditorDebugger::_get_res_path_cache(const String& p_path) {
+
+ Map<String,int>::Element *E=res_path_cache.find(p_path);
+
+ if (E)
+ return E->get();
+
+ last_path_id++;
+
+ res_path_cache[p_path]=last_path_id;
+ Array msg;
+ msg.push_back("live_res_path");
+ msg.push_back(p_path);
+ msg.push_back(last_path_id);
+ ppeer->put_var(msg);
+
+
+ return last_path_id;
+}
+
+void ScriptEditorDebugger::_method_changed(Object*p_base,const StringName& p_name,VARIANT_ARG_DECLARE) {
+
+ if (!p_base || !live_debug || !connection.is_valid() || !editor->get_edited_scene())
+ return;
+
+ Node *node = p_base->cast_to<Node>();
+
+ VARIANT_ARGPTRS
+
+ for(int i=0;i<VARIANT_ARG_MAX;i++) {
+ //no pointers, sorry
+ if (argptr[i] && (argptr[i]->get_type()==Variant::OBJECT || argptr[i]->get_type()==Variant::_RID))
+ return;
+ }
+
+ if (node) {
+
+ NodePath path = editor->get_edited_scene()->get_path_to(node);
+ int pathid = _get_node_path_cache(path);
+
+
+
+ Array msg;
+ msg.push_back("live_node_call");
+ msg.push_back(pathid);
+ msg.push_back(p_name);
+ for(int i=0;i<VARIANT_ARG_MAX;i++) {
+ //no pointers, sorry
+ msg.push_back(*argptr[i]);
+ }
+ ppeer->put_var(msg);
+
+ return;
+
+ }
+
+ Resource *res = p_base->cast_to<Resource>();
+
+ if (res && res->get_path()!=String()) {
+
+ String respath = res->get_path();
+ int pathid = _get_res_path_cache(respath);
+
+ Array msg;
+ msg.push_back("live_res_call");
+ msg.push_back(pathid);
+ msg.push_back(p_name);
+ for(int i=0;i<VARIANT_ARG_MAX;i++) {
+ //no pointers, sorry
+ msg.push_back(*argptr[i]);
+ }
+ ppeer->put_var(msg);
+
+ return;
+ }
+
+ //print_line("method");
+}
+
+void ScriptEditorDebugger::_property_changed(Object*p_base,const StringName& p_property,const Variant& p_value){
+
+ if (!p_base || !live_debug || !connection.is_valid() || !editor->get_edited_scene())
+ return;
+
+ Node *node = p_base->cast_to<Node>();
+
+ if (node) {
+
+ NodePath path = editor->get_edited_scene()->get_path_to(node);
+ int pathid = _get_node_path_cache(path);
+
+
+ if (p_value.is_ref()) {
+ Ref<Resource> res = p_value;
+ if (res.is_valid() && res->get_path()!=String()) {
+
+ Array msg;
+ msg.push_back("live_node_prop_res");
+ msg.push_back(pathid);
+ msg.push_back(p_property);
+ msg.push_back(res->get_path());
+ ppeer->put_var(msg);
+ }
+ } else {
+
+ Array msg;
+ msg.push_back("live_node_prop");
+ msg.push_back(pathid);
+ msg.push_back(p_property);
+ msg.push_back(p_value);
+ ppeer->put_var(msg);
+ }
+
+
+ return;
+
+ }
+
+ Resource *res = p_base->cast_to<Resource>();
+
+ if (res && res->get_path()!=String()) {
+
+ String respath = res->get_path();
+ int pathid = _get_res_path_cache(respath);
+
+
+ if (p_value.is_ref()) {
+ Ref<Resource> res = p_value;
+ if (res.is_valid() && res->get_path()!=String()) {
+
+ Array msg;
+ msg.push_back("live_res_prop_res");
+ msg.push_back(pathid);
+ msg.push_back(p_property);
+ msg.push_back(res->get_path());
+ ppeer->put_var(msg);
+ }
+ } else {
+
+ Array msg;
+ msg.push_back("live_res_prop");
+ msg.push_back(pathid);
+ msg.push_back(p_property);
+ msg.push_back(p_value);
+ ppeer->put_var(msg);
+ }
+
+
+ return;
+ }
+
+
+ //print_line("prop");
+}
+
+void ScriptEditorDebugger::_method_changeds(void *p_ud,Object*p_base,const StringName& p_name,VARIANT_ARG_DECLARE) {
+
+ ScriptEditorDebugger *sed = (ScriptEditorDebugger*)p_ud;
+ sed->_method_changed(p_base,p_name,VARIANT_ARG_PASS);
+
+
+}
+
+void ScriptEditorDebugger::_property_changeds(void *p_ud,Object*p_base,const StringName& p_property,const Variant& p_value){
+
+ ScriptEditorDebugger *sed = (ScriptEditorDebugger*)p_ud;
+ sed->_property_changed(p_base,p_property,p_value);
+
+}
+
+void ScriptEditorDebugger::set_live_debugging(bool p_enable) {
+
+ live_debug=p_enable;
+}
+
+void ScriptEditorDebugger::_live_edit_set() {
+
+ if (!connection.is_valid())
+ return;
+
+ TreeItem* ti = scene_tree->get_selected();
+ if (!ti)
+ return;
+ String path;
+
+ while(ti) {
+ String lp=ti->get_text(0);
+ path="/"+lp+path;
+ ti=ti->get_parent();
+
+ }
+
+ NodePath np = path;
+
+ editor->get_editor_data().set_edited_scene_live_edit_root(np);
+
+ update_live_edit_root();
+
+
+}
+
+void ScriptEditorDebugger::_live_edit_clear() {
+
+ NodePath np = NodePath("/root");
+ editor->get_editor_data().set_edited_scene_live_edit_root(np);
+
+ update_live_edit_root();
+
+}
+
+void ScriptEditorDebugger::update_live_edit_root() {
+
+ NodePath np = editor->get_editor_data().get_edited_scene_live_edit_root();
+
+ if (connection.is_valid()) {
+ Array msg;
+ msg.push_back("live_set_root");
+ msg.push_back(np);
+ if (editor->get_edited_scene())
+ msg.push_back(editor->get_edited_scene()->get_filename());
+ else
+ msg.push_back("");
+ ppeer->put_var(msg);
+ }
+ live_edit_root->set_text(np);
+
+}
+
+void ScriptEditorDebugger::live_debug_create_node(const NodePath& p_parent,const String& p_type,const String& p_name) {
+
+ if (live_debug && connection.is_valid()) {
+ Array msg;
+ msg.push_back("live_create_node");
+ msg.push_back(p_parent);
+ msg.push_back(p_type);
+ msg.push_back(p_name);
+ ppeer->put_var(msg);
+ }
+}
+
+void ScriptEditorDebugger::live_debug_instance_node(const NodePath& p_parent,const String& p_path,const String& p_name){
+
+ if (live_debug && connection.is_valid()) {
+ Array msg;
+ msg.push_back("live_instance_node");
+ msg.push_back(p_parent);
+ msg.push_back(p_path);
+ msg.push_back(p_name);
+ ppeer->put_var(msg);
+ }
+
+}
+void ScriptEditorDebugger::live_debug_remove_node(const NodePath& p_at){
+
+ if (live_debug && connection.is_valid()) {
+ Array msg;
+ msg.push_back("live_remove_node");
+ msg.push_back(p_at);
+ ppeer->put_var(msg);
+ }
+
+}
+void ScriptEditorDebugger::live_debug_remove_and_keep_node(const NodePath& p_at,ObjectID p_keep_id) {
+
+ if (live_debug && connection.is_valid()) {
+ Array msg;
+ msg.push_back("live_remove_and_keep_node");
+ msg.push_back(p_at);
+ msg.push_back(p_keep_id);
+ ppeer->put_var(msg);
+ }
+
+}
+void ScriptEditorDebugger::live_debug_restore_node(ObjectID p_id, const NodePath& p_at, int p_at_pos){
+
+ if (live_debug && connection.is_valid()) {
+ Array msg;
+ msg.push_back("live_restore_node");
+ msg.push_back(p_id);
+ msg.push_back(p_at);
+ msg.push_back(p_at_pos);
+ ppeer->put_var(msg);
+ }
+
+}
+void ScriptEditorDebugger::live_debug_duplicate_node(const NodePath& p_at,const String& p_new_name){
+
+ if (live_debug && connection.is_valid()) {
+ Array msg;
+ msg.push_back("live_duplicate_node");
+ msg.push_back(p_at);
+ msg.push_back(p_new_name);
+ ppeer->put_var(msg);
+ }
+
+}
+void ScriptEditorDebugger::live_debug_reparent_node(const NodePath& p_at, const NodePath& p_new_place, const String &p_new_name, int p_at_pos){
+
+ if (live_debug && connection.is_valid()) {
+ Array msg;
+ msg.push_back("live_reparent_node");
+ msg.push_back(p_at);
+ msg.push_back(p_new_place);
+ msg.push_back(p_new_name);
+ msg.push_back(p_at_pos);
+ ppeer->put_var(msg);
+ }
+
+}
+
+void ScriptEditorDebugger::set_breakpoint(const String& p_path,int p_line,bool p_enabled) {
+
+ if (connection.is_valid()) {
+ Array msg;
+ msg.push_back("breakpoint");
+ msg.push_back(p_path);
+ msg.push_back(p_line);
+ msg.push_back(p_enabled);
+ ppeer->put_var(msg);
+ }
+}
+
+
+void ScriptEditorDebugger::_error_selected(int p_idx) {
+
+ error_stack->clear();
+ Array st=error_list->get_item_metadata(p_idx);
+ for(int i=0;i<st.size();i+=2) {
+
+ String script=st[i];
+ int line=st[i+1];
+ Array md;
+ md.push_back(st[i]);
+ md.push_back(st[i+1]);
+
+ String str = script.get_file()+":"+itos(line);
+
+ error_stack->add_item(str);
+ error_stack->set_item_metadata(error_stack->get_item_count()-1,md);
+ error_stack->set_item_tooltip(error_stack->get_item_count()-1,"File: "+String(st[i])+"\nLine: "+itos(line));
+ }
+}
+
+void ScriptEditorDebugger:: _error_stack_selected(int p_idx){
+
+ Array arr = error_stack->get_item_metadata(p_idx);
+ if (arr.size()!=2)
+ return;
+
+
+ Ref<Script> s = ResourceLoader::load(arr[0]);
+ emit_signal("goto_script_line",s,int(arr[1])-1);
+
+}
+
+
void ScriptEditorDebugger::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_stack_dump_frame_selected"),&ScriptEditorDebugger::_stack_dump_frame_selected);
@@ -678,6 +1173,20 @@ void ScriptEditorDebugger::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_performance_draw"),&ScriptEditorDebugger::_performance_draw);
ObjectTypeDB::bind_method(_MD("_performance_select"),&ScriptEditorDebugger::_performance_select);
ObjectTypeDB::bind_method(_MD("_scene_tree_request"),&ScriptEditorDebugger::_scene_tree_request);
+ ObjectTypeDB::bind_method(_MD("_video_mem_request"),&ScriptEditorDebugger::_video_mem_request);
+ ObjectTypeDB::bind_method(_MD("_live_edit_set"),&ScriptEditorDebugger::_live_edit_set);
+ ObjectTypeDB::bind_method(_MD("_live_edit_clear"),&ScriptEditorDebugger::_live_edit_clear);
+
+ ObjectTypeDB::bind_method(_MD("_error_selected"),&ScriptEditorDebugger::_error_selected);
+ ObjectTypeDB::bind_method(_MD("_error_stack_selected"),&ScriptEditorDebugger::_error_stack_selected);
+
+ ObjectTypeDB::bind_method(_MD("live_debug_create_node"),&ScriptEditorDebugger::live_debug_create_node);
+ ObjectTypeDB::bind_method(_MD("live_debug_instance_node"),&ScriptEditorDebugger::live_debug_instance_node);
+ ObjectTypeDB::bind_method(_MD("live_debug_remove_node"),&ScriptEditorDebugger::live_debug_remove_node);
+ ObjectTypeDB::bind_method(_MD("live_debug_remove_and_keep_node"),&ScriptEditorDebugger::live_debug_remove_and_keep_node);
+ ObjectTypeDB::bind_method(_MD("live_debug_restore_node"),&ScriptEditorDebugger::live_debug_restore_node);
+ ObjectTypeDB::bind_method(_MD("live_debug_duplicate_node"),&ScriptEditorDebugger::live_debug_duplicate_node);
+ ObjectTypeDB::bind_method(_MD("live_debug_reparent_node"),&ScriptEditorDebugger::live_debug_reparent_node);
ADD_SIGNAL(MethodInfo("goto_script_line"));
ADD_SIGNAL(MethodInfo("breaked",PropertyInfo(Variant::BOOL,"reallydid")));
@@ -791,6 +1300,23 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor){
vbc->add_child(hbc);
+ error_split = memnew( HSplitContainer );
+ VBoxContainer *errvb = memnew( VBoxContainer );
+ errvb->set_h_size_flags(SIZE_EXPAND_FILL);
+ error_list = memnew( ItemList );
+ errvb->add_margin_child("Errors:",error_list,true);
+ error_split->add_child(errvb);
+
+ errvb = memnew( VBoxContainer );
+ errvb->set_h_size_flags(SIZE_EXPAND_FILL);
+ error_stack = memnew( ItemList );
+ errvb->add_margin_child("Stack Trace (if applies):",error_stack,true);
+ error_split->add_child(errvb);
+
+ error_split->set_name("Errors");
+ tabs->add_child(error_split);
+
+
HSplitContainer *hsp = memnew( HSplitContainer );
perf_monitors = memnew(Tree);
@@ -834,6 +1360,47 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor){
}
+ VBoxContainer *vmem_vb = memnew( VBoxContainer );
+ HBoxContainer *vmem_hb = memnew( HBoxContainer );
+ Label *vmlb = memnew(Label("List of Video Memory Usage by Resource: ") );
+ vmlb->set_h_size_flags(SIZE_EXPAND_FILL);
+ vmem_hb->add_child( vmlb );
+ vmem_hb->add_child( memnew(Label("Total: ")) );
+ vmem_total = memnew( LineEdit );
+ vmem_total->set_editable(false);
+ vmem_total->set_custom_minimum_size(Size2(100,1));
+ vmem_hb->add_child(vmem_total);
+ vmem_refresh = memnew( Button );
+ vmem_hb->add_child(vmem_refresh);
+ vmem_vb->add_child(vmem_hb);
+ vmem_refresh->connect("pressed",this,"_video_mem_request");
+
+ MarginContainer *vmmc = memnew( MarginContainer );
+ vmem_tree = memnew( Tree );
+ vmem_tree->set_v_size_flags(SIZE_EXPAND_FILL);
+ vmem_tree->set_h_size_flags(SIZE_EXPAND_FILL);
+ vmmc->add_child(vmem_tree);
+ vmmc->set_v_size_flags(SIZE_EXPAND_FILL);
+ vmem_vb->add_child(vmmc);
+
+ vmem_vb->set_name("Video Mem");
+ vmem_tree->set_columns(4);
+ vmem_tree->set_column_titles_visible(true);
+ vmem_tree->set_column_title(0,"Resource Path");
+ vmem_tree->set_column_expand(0,true);
+ vmem_tree->set_column_expand(1,false);
+ vmem_tree->set_column_title(1,"Type");
+ vmem_tree->set_column_min_width(1,100);
+ vmem_tree->set_column_expand(2,false);
+ vmem_tree->set_column_title(2,"Format");
+ vmem_tree->set_column_min_width(2,150);
+ vmem_tree->set_column_expand(3,false);
+ vmem_tree->set_column_title(3,"Usage");
+ vmem_tree->set_column_min_width(3,80);
+ vmem_tree->set_hide_root(true);
+
+ tabs->add_child(vmem_vb);
+
info = memnew( HSplitContainer );
info->set_name("Info");
tabs->add_child(info);
@@ -845,6 +1412,26 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor){
info_left->add_margin_child("Clicked Control:",clicked_ctrl);
clicked_ctrl_type = memnew( LineEdit );
info_left->add_margin_child("Clicked Control Type:",clicked_ctrl_type);
+
+ live_edit_root = memnew( LineEdit );
+
+ {
+ HBoxContainer *lehb = memnew( HBoxContainer );
+ Label *l = memnew( Label("Live Edit Root:") );
+ lehb->add_child(l);
+ l->set_h_size_flags(SIZE_EXPAND_FILL);
+ le_set = memnew( Button("Set From Tree") );
+ lehb->add_child(le_set);
+ le_clear = memnew( Button("Clear") );
+ lehb->add_child(le_clear);
+ info_left->add_child(lehb);
+ MarginContainer *mc = memnew( MarginContainer );
+ mc->add_child(live_edit_root);
+ info_left->add_child(mc);
+ le_set->set_disabled(true);
+ le_clear->set_disabled(true);
+ }
+
VBoxContainer *info_right = memnew(VBoxContainer);
info_right->set_h_size_flags(SIZE_EXPAND_FILL);
info->add_child(info_right);
@@ -870,6 +1457,14 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor){
hide();
log_forced_visible=false;
+ p_editor->get_undo_redo()->set_method_notify_callback(_method_changeds,this);
+ p_editor->get_undo_redo()->set_property_notify_callback(_property_changeds,this);
+ live_debug=false;
+ last_path_id=false;
+ error_count=0;
+ last_error_count=0;
+
+
}
ScriptEditorDebugger::~ScriptEditorDebugger() {
diff --git a/tools/editor/script_editor_debugger.h b/tools/editor/script_editor_debugger.h
index 5127a329aa..6b66a62dd5 100644
--- a/tools/editor/script_editor_debugger.h
+++ b/tools/editor/script_editor_debugger.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -26,110 +26,166 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SCRIPT_EDITOR_DEBUGGER_H
-#define SCRIPT_EDITOR_DEBUGGER_H
-
-#include "scene/gui/box_container.h"
-#include "scene/gui/button.h"
-#include "core/io/tcp_server.h"
-#include "core/io/packet_peer.h"
-
-class Tree;
-class PropertyEditor;
-class EditorNode;
-class ScriptEditorDebuggerVariables;
-class LineEdit;
-class TabContainer;
-class RichTextLabel;
-class TextureButton;
-class AcceptDialog;
-class TreeItem;
-class HSplitContainer;
-
-class ScriptEditorDebugger : public Control {
-
- OBJ_TYPE( ScriptEditorDebugger, Control );
-
- AcceptDialog *msgdialog;
-
-
-
- LineEdit *clicked_ctrl;
- LineEdit *clicked_ctrl_type;
- Tree *scene_tree;
- HSplitContainer *info;
- Button *scene_tree_refresh;
-
- TextureButton *tb;
-
-
- TabContainer *tabs;
-
- Label *reason;
- bool log_forced_visible;
- ScriptEditorDebuggerVariables *variables;
-
- Button *step;
- Button *next;
- Button *back;
- Button *forward;
- Button *dobreak;
- Button *docontinue;
-
- List<Vector<float> > perf_history;
- Vector<float> perf_max;
- Vector<TreeItem*> perf_items;
-
- Tree *perf_monitors;
- Control *perf_draw;
-
- Tree *stack_dump;
- PropertyEditor *inspector;
-
- Ref<TCP_Server> server;
- Ref<StreamPeerTCP> connection;
- Ref<PacketPeerStream> ppeer;
-
- String message_type;
- Array message;
- int pending_in_queue;
-
-
- EditorNode *editor;
-
- bool breaked;
-
- void _performance_draw();
- void _performance_select(Object *, int, bool);
- void _stack_dump_frame_selected();
- void _output_clear();
- void _hide_request();
-
- void _scene_tree_request();
- void _parse_message(const String& p_msg,const Array& p_data);
-
-protected:
-
- void _notification(int p_what);
- static void _bind_methods();
-
-public:
-
- void start();
- void pause();
- void unpause();
- void stop();
-
- void debug_next();
- void debug_step();
- void debug_break();
- void debug_continue();
-
- String get_var_value(const String& p_var) const;
-
- virtual Size2 get_minimum_size() const;
- ScriptEditorDebugger(EditorNode *p_editor=NULL);
- ~ScriptEditorDebugger();
-};
-
-#endif // SCRIPT_EDITOR_DEBUGGER_H
+#ifndef SCRIPT_EDITOR_DEBUGGER_H
+#define SCRIPT_EDITOR_DEBUGGER_H
+
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "core/io/tcp_server.h"
+#include "core/io/packet_peer.h"
+
+class Tree;
+class PropertyEditor;
+class EditorNode;
+class ScriptEditorDebuggerVariables;
+class LineEdit;
+class TabContainer;
+class RichTextLabel;
+class TextureButton;
+class AcceptDialog;
+class TreeItem;
+class HSplitContainer;
+class ItemList;
+
+class ScriptEditorDebugger : public Control {
+
+ OBJ_TYPE( ScriptEditorDebugger, Control );
+
+ AcceptDialog *msgdialog;
+
+
+
+ LineEdit *clicked_ctrl;
+ LineEdit *clicked_ctrl_type;
+ LineEdit *live_edit_root;
+ Tree *scene_tree;
+ HSplitContainer *info;
+ Button *scene_tree_refresh;
+ Button *le_set;
+ Button *le_clear;
+
+ HSplitContainer *error_split;
+ ItemList *error_list;
+ ItemList *error_stack;
+
+ int error_count;
+ int last_error_count;
+
+
+
+ TextureButton *tb;
+
+
+ TabContainer *tabs;
+
+ Label *reason;
+ bool log_forced_visible;
+ ScriptEditorDebuggerVariables *variables;
+
+ Button *step;
+ Button *next;
+ Button *back;
+ Button *forward;
+ Button *dobreak;
+ Button *docontinue;
+
+ List<Vector<float> > perf_history;
+ Vector<float> perf_max;
+ Vector<TreeItem*> perf_items;
+
+ Tree *perf_monitors;
+ Control *perf_draw;
+
+ Tree *vmem_tree;
+ Button *vmem_refresh;
+ LineEdit *vmem_total;
+
+ Tree *stack_dump;
+ PropertyEditor *inspector;
+
+ Ref<TCP_Server> server;
+ Ref<StreamPeerTCP> connection;
+ Ref<PacketPeerStream> ppeer;
+
+ String message_type;
+ Array message;
+ int pending_in_queue;
+
+ HashMap<NodePath,int> node_path_cache;
+ int last_path_id;
+ Map<String,int> res_path_cache;
+
+
+ EditorNode *editor;
+
+ bool breaked;
+
+ bool live_debug;
+
+ void _performance_draw();
+ void _performance_select(Object *, int, bool);
+ void _stack_dump_frame_selected();
+ void _output_clear();
+ void _hide_request();
+
+ void _scene_tree_request();
+ void _parse_message(const String& p_msg,const Array& p_data);
+
+ void _video_mem_request();
+
+ int _get_node_path_cache(const NodePath& p_path);
+
+ int _get_res_path_cache(const String& p_path);
+
+ void _live_edit_set();
+ void _live_edit_clear();
+
+ void _method_changed(Object*p_base,const StringName& p_name,VARIANT_ARG_DECLARE);
+ void _property_changed(Object*p_base,const StringName& p_property,const Variant& p_value);
+
+ static void _method_changeds(void *p_ud,Object*p_base,const StringName& p_name,VARIANT_ARG_DECLARE);
+ static void _property_changeds(void *p_ud,Object*p_base,const StringName& p_property,const Variant& p_value);
+
+ void _error_selected(int p_idx);
+ void _error_stack_selected(int p_idx);
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ void start();
+ void pause();
+ void unpause();
+ void stop();
+
+ void debug_next();
+ void debug_step();
+ void debug_break();
+ void debug_continue();
+
+ String get_var_value(const String& p_var) const;
+
+ void set_live_debugging(bool p_enable);
+
+ void live_debug_create_node(const NodePath& p_parent,const String& p_type,const String& p_name);
+ void live_debug_instance_node(const NodePath& p_parent,const String& p_path,const String& p_name);
+ void live_debug_remove_node(const NodePath& p_at);
+ void live_debug_remove_and_keep_node(const NodePath& p_at,ObjectID p_keep_id);
+ void live_debug_restore_node(ObjectID p_id,const NodePath& p_at,int p_at_pos);
+ void live_debug_duplicate_node(const NodePath& p_at,const String& p_new_name);
+ void live_debug_reparent_node(const NodePath& p_at,const NodePath& p_new_place,const String& p_new_name,int p_at_pos);
+
+ void set_breakpoint(const String& p_path,int p_line,bool p_enabled);
+
+ void update_live_edit_root();
+
+
+ virtual Size2 get_minimum_size() const;
+ ScriptEditorDebugger(EditorNode *p_editor=NULL);
+ ~ScriptEditorDebugger();
+};
+
+#endif // SCRIPT_EDITOR_DEBUGGER_H
diff --git a/tools/editor/settings_config_dialog.cpp b/tools/editor/settings_config_dialog.cpp
index 2310df4ffb..6d8f849427 100644
--- a/tools/editor/settings_config_dialog.cpp
+++ b/tools/editor/settings_config_dialog.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -72,6 +72,10 @@ void EditorSettingsDialog::popup_edit_settings() {
property_editor->edit(EditorSettings::get_singleton());
property_editor->update_tree();
+
+ search_box->select_all();
+ search_box->grab_focus();
+
popup_centered_ratio(0.7);
}
@@ -244,11 +248,21 @@ void EditorSettingsDialog::_update_plugins() {
}
+void EditorSettingsDialog::_clear_search_box() {
+
+ if (search_box->get_text()=="")
+ return;
+
+ search_box->clear();
+ property_editor->update_tree();
+}
+
void EditorSettingsDialog::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_TREE) {
rescan_plugins->set_icon(get_icon("Reload","EditorIcons"));
+ clear_button->set_icon(get_icon("Close","EditorIcons"));
_update_plugins();
}
}
@@ -261,6 +275,7 @@ void EditorSettingsDialog::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_plugin_settings"),&EditorSettingsDialog::_plugin_settings);
ObjectTypeDB::bind_method(_MD("_plugin_edited"),&EditorSettingsDialog::_plugin_edited);
ObjectTypeDB::bind_method(_MD("_plugin_install"),&EditorSettingsDialog::_plugin_install);
+ ObjectTypeDB::bind_method(_MD("_clear_search_box"),&EditorSettingsDialog::_clear_search_box);
}
EditorSettingsDialog::EditorSettingsDialog() {
@@ -271,16 +286,38 @@ EditorSettingsDialog::EditorSettingsDialog() {
add_child(tabs);
set_child_rect(tabs);
+ VBoxContainer *vbc = memnew( VBoxContainer );
+ tabs->add_child(vbc);
+ vbc->set_name("General");
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ vbc->add_child(hbc);
+
+ Label *l = memnew( Label );
+ l->set_text("Search: ");
+ hbc->add_child(l);
+
+ search_box = memnew( LineEdit );
+ search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ hbc->add_child(search_box);
+
+ clear_button = memnew( ToolButton );
+ hbc->add_child(clear_button);
+ clear_button->connect("pressed",this,"_clear_search_box");
+
property_editor = memnew( PropertyEditor );
property_editor->hide_top_label();
- tabs->add_child(property_editor);
- property_editor->set_name("General");
+ property_editor->set_use_filter(true);
+ property_editor->register_text_enter(search_box);
+ property_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ vbc->add_child(property_editor);
- VBoxContainer *vbc = memnew( VBoxContainer );
+ vbc = memnew( VBoxContainer );
tabs->add_child(vbc);
vbc->set_name("Plugins");
- HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc = memnew( HBoxContainer );
vbc->add_child(hbc);
hbc->add_child( memnew( Label("Plugin List: ")));
hbc->add_spacer();
diff --git a/tools/editor/settings_config_dialog.h b/tools/editor/settings_config_dialog.h
index a3e4a87faf..50159cf488 100644
--- a/tools/editor/settings_config_dialog.h
+++ b/tools/editor/settings_config_dialog.h
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -51,6 +51,8 @@ class EditorSettingsDialog : public AcceptDialog {
Button *rescan_plugins;
Tree *plugins;
+ LineEdit *search_box;
+ ToolButton *clear_button;
PropertyEditor *property_editor;
Timer *timer;
@@ -71,6 +73,8 @@ class EditorSettingsDialog : public AcceptDialog {
void _rescan_plugins();
void _update_plugins();
+ void _clear_search_box();
+
protected:
static void _bind_methods();
diff --git a/tools/editor/spatial_editor_gizmos.cpp b/tools/editor/spatial_editor_gizmos.cpp
index d48a4ce813..5efca44c7d 100644
--- a/tools/editor/spatial_editor_gizmos.cpp
+++ b/tools/editor/spatial_editor_gizmos.cpp
@@ -1,3190 +1,3191 @@
-/*************************************************************************/
-/* spatial_editor_gizmos.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* http://www.godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-#include "spatial_editor_gizmos.h"
-#include "geometry.h"
-#include "scene/3d/camera.h"
-#include "scene/resources/surface_tool.h"
-#include "scene/resources/sphere_shape.h"
-#include "scene/resources/box_shape.h"
-#include "scene/resources/capsule_shape.h"
-#include "scene/resources/ray_shape.h"
-#include "scene/resources/convex_polygon_shape.h"
-#include "scene/resources/plane_shape.h"
-#include "quick_hull.h"
-
-// Keep small children away from this file.
-// It's so ugly it will eat them alive
-
-#define HANDLE_HALF_SIZE 0.05
-
-void SpatialGizmoTool::clear() {
-
- for(int i=0;i<instances.size();i++) {
-
- if (instances[i].instance.is_valid())
- VS::get_singleton()->free(instances[i].instance);
-
-
- }
-
- billboard_handle=false;
- collision_segments.clear();
- collision_mesh=Ref<TriangleMesh>();
- instances.clear();
- handles.clear();
- secondary_handles.clear();
-}
-
-void SpatialGizmoTool::Instance::create_instance(Spatial *p_base) {
-
- instance = VS::get_singleton()->instance_create2(mesh->get_rid(),p_base->get_world()->get_scenario());
- VS::get_singleton()->instance_attach_object_instance_ID(instance,p_base->get_instance_ID());
- if (billboard)
- VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_BILLBOARD,true);
- if (unscaled)
- VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_DEPH_SCALE,true);
- if (skeleton.is_valid())
- VS::get_singleton()->instance_attach_skeleton(instance,skeleton);
- if (extra_margin)
- VS::get_singleton()->instance_set_extra_visibility_margin(instance,1);
- VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_CAST_SHADOW,false);
- VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_RECEIVE_SHADOWS,false);
- VS::get_singleton()->instance_set_layer_mask(instance,1<<SpatialEditorViewport::GIZMO_EDIT_LAYER); //gizmos are 26
-}
-
-
-
-void SpatialGizmoTool::add_mesh(const Ref<Mesh>& p_mesh,bool p_billboard, const RID &p_skeleton) {
-
- ERR_FAIL_COND(!spatial_node);
- Instance ins;
-
- ins.billboard=p_billboard;
- ins.mesh=p_mesh;
- ins.skeleton=p_skeleton;
- if (valid) {
- ins.create_instance(spatial_node);
- VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform());
- }
-
- instances.push_back(ins);
-
-}
-
-void SpatialGizmoTool::add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material,bool p_billboard){
-
- ERR_FAIL_COND(!spatial_node);
- Instance ins;
-
- Ref<Mesh> mesh = memnew( Mesh );
- Array a;
- a.resize(Mesh::ARRAY_MAX);
-
- a[Mesh::ARRAY_VERTEX]=p_lines;
-
- DVector<Color> color;
- color.resize(p_lines.size());
- {
- DVector<Color>::Write w = color.write();
- for(int i=0;i<p_lines.size();i++) {
- if (is_selected())
- w[i]=Color(1,1,1,0.6);
- else
- w[i]=Color(1,1,1,0.25);
- }
-
- }
-
- a[Mesh::ARRAY_COLOR]=color;
-
-
- mesh->add_surface(Mesh::PRIMITIVE_LINES,a);
- mesh->surface_set_material(0,p_material);
-
- if (p_billboard) {
- float md=0;
- for(int i=0;i<p_lines.size();i++) {
-
- md=MAX(0,p_lines[i].length());
-
- }
- if (md) {
- mesh->set_custom_aabb(AABB(Vector3(-md,-md,-md),Vector3(md,md,md)*2.0));
- }
- }
-
- ins.billboard=p_billboard;
- ins.mesh=mesh;
- if (valid) {
- ins.create_instance(spatial_node);
- VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform());
- }
-
- instances.push_back(ins);
-
-}
-
-void SpatialGizmoTool::add_unscaled_billboard(const Ref<Material>& p_material,float p_scale) {
-
- ERR_FAIL_COND(!spatial_node);
- Instance ins;
-
- Vector<Vector3 > vs;
- Vector<Vector2 > uv;
-
- vs.push_back(Vector3(-p_scale,p_scale,0));
- vs.push_back(Vector3(p_scale,p_scale,0));
- vs.push_back(Vector3(p_scale,-p_scale,0));
- vs.push_back(Vector3(-p_scale,-p_scale,0));
-
- uv.push_back(Vector2(1,0));
- uv.push_back(Vector2(0,0));
- uv.push_back(Vector2(0,1));
- uv.push_back(Vector2(1,1));
-
- Ref<Mesh> mesh = memnew( Mesh );
- Array a;
- a.resize(Mesh::ARRAY_MAX);
- a[Mesh::ARRAY_VERTEX]=vs;
- a[Mesh::ARRAY_TEX_UV]=uv;
- mesh->add_surface(Mesh::PRIMITIVE_TRIANGLE_FAN,a);
- mesh->surface_set_material(0,p_material);
-
- if (true) {
- float md=0;
- for(int i=0;i<vs.size();i++) {
-
- md=MAX(0,vs[i].length());
-
- }
- if (md) {
- mesh->set_custom_aabb(AABB(Vector3(-md,-md,-md),Vector3(md,md,md)*2.0));
- }
- }
-
- ins.mesh=mesh;
- ins.unscaled=true;
- ins.billboard=true;
- if (valid) {
- ins.create_instance(spatial_node);
- VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform());
- }
-
- instances.push_back(ins);
-
-
-}
-
-void SpatialGizmoTool::add_collision_triangles(const Ref<TriangleMesh>& p_tmesh) {
-
- collision_mesh=p_tmesh;
-}
-
-void SpatialGizmoTool::add_collision_segments(const Vector<Vector3> &p_lines) {
-
- int from=collision_segments.size();
- collision_segments.resize(from+p_lines.size());
- for(int i=0;i<p_lines.size();i++) {
-
- collision_segments[from+i]=p_lines[i];
- }
-}
-
-
-void SpatialGizmoTool::add_handles(const Vector<Vector3> &p_handles, bool p_billboard,bool p_secondary){
-
- billboard_handle=p_billboard;
-
- if (!is_selected())
- return;
-
- ERR_FAIL_COND(!spatial_node);
-
- ERR_FAIL_COND(!spatial_node);
- Instance ins;
-
-
- Ref<Mesh> mesh = memnew( Mesh );
-#if 1
-
- Array a;
- a.resize(VS::ARRAY_MAX);
- a[VS::ARRAY_VERTEX]=p_handles;
- DVector<Color> colors;
- {
- colors.resize(p_handles.size());
- DVector<Color>::Write w=colors.write();
- for(int i=0;i<p_handles.size();i++) {
-
- Color col(1,1,1,1);
- if (SpatialEditor::get_singleton()->get_over_gizmo_handle()!=i)
- col=Color(0.9,0.9,0.9,0.9);
- w[i]=col;
- }
-
- }
- a[VS::ARRAY_COLOR]=colors;
- mesh->add_surface(Mesh::PRIMITIVE_POINTS,a);
- mesh->surface_set_material(0,SpatialEditorGizmos::singleton->handle2_material);
-
- if (p_billboard) {
- float md=0;
- for(int i=0;i<p_handles.size();i++) {
-
- md=MAX(0,p_handles[i].length());
-
- }
- if (md) {
- mesh->set_custom_aabb(AABB(Vector3(-md,-md,-md),Vector3(md,md,md)*2.0));
- }
- }
-
-
-
-#else
- for(int ih=0;ih<p_handles.size();ih++) {
-
-
- Vector<Vector3> vertices;
- Vector<Vector3> normals;
-
- int vtx_idx=0;
-#define ADD_VTX(m_idx);\
- vertices.push_back( (face_points[m_idx]*HANDLE_HALF_SIZE+p_handles[ih]) );\
- normals.push_back( normal_points[m_idx] );\
- vtx_idx++;\
-
- for (int i=0;i<6;i++) {
-
-
- Vector3 face_points[4];
- Vector3 normal_points[4];
- float uv_points[8]={0,0,0,1,1,1,1,0};
-
- for (int j=0;j<4;j++) {
-
- float v[3];
- v[0]=1.0;
- v[1]=1-2*((j>>1)&1);
- v[2]=v[1]*(1-2*(j&1));
-
- for (int k=0;k<3;k++) {
-
- if (i<3)
- face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
- else
- face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
- }
- normal_points[j]=Vector3();
- normal_points[j][i%3]=(i>=3?-1:1);
- }
- //tri 1
- ADD_VTX(0);
- ADD_VTX(1);
- ADD_VTX(2);
- //tri 2
- ADD_VTX(2);
- ADD_VTX(3);
- ADD_VTX(0);
-
- }
-
-
- Array d;
- d.resize(VS::ARRAY_MAX);
- d[VisualServer::ARRAY_NORMAL]= normals ;
- d[VisualServer::ARRAY_VERTEX]= vertices ;
-
- mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,d);
- mesh->surface_set_material(ih,SpatialEditorGizmos::singleton->handle_material);
-
-
- }
-#endif
- ins.mesh=mesh;
- ins.billboard=p_billboard;
- ins.extra_margin=true;
- if (valid) {
- ins.create_instance(spatial_node);
- VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform());
- }
- instances.push_back(ins);
- if (!p_secondary) {
- int chs=handles.size();
- handles.resize(chs+p_handles.size());
- for(int i=0;i<p_handles.size();i++) {
- handles[i+chs]=p_handles[i];
- }
- } else {
-
- int chs=secondary_handles.size();
- secondary_handles.resize(chs+p_handles.size());
- for(int i=0;i<p_handles.size();i++) {
- secondary_handles[i+chs]=p_handles[i];
- }
-
- }
-
-}
-
-
-void SpatialGizmoTool::set_spatial_node(Spatial *p_node){
-
- spatial_node=p_node;
-
-}
-
-bool SpatialGizmoTool::intersect_frustum(const Camera *p_camera,const Vector<Plane> &p_frustum) {
-
- ERR_FAIL_COND_V(!spatial_node,false);
- ERR_FAIL_COND_V(!valid,false);
-
- if (collision_segments.size()) {
-
- const Plane *p=p_frustum.ptr();
- int fc=p_frustum.size();
-
- int vc=collision_segments.size();
- const Vector3* vptr=collision_segments.ptr();
- Transform t = spatial_node->get_global_transform();
-
- for(int i=0;i<vc/2;i++) {
-
-
- Vector3 a=t.xform(vptr[i*2+0]);
- Vector3 b=t.xform(vptr[i*2+1]);
-
- bool any_out=false;
- for(int j=0;j<fc;j++) {
-
- if (p[j].distance_to(a) > 0 && p[j].distance_to(b) >0) {
-
- any_out=true;
- break;
- }
- }
-
- if (!any_out)
- return true;
- }
-
- return false;
- }
-
- return false;
-}
-
-
-bool SpatialGizmoTool::intersect_ray(const Camera *p_camera,const Point2& p_point, Vector3& r_pos, Vector3& r_normal,int *r_gizmo_handle,bool p_sec_first) {
-
- ERR_FAIL_COND_V(!spatial_node,false);
- ERR_FAIL_COND_V(!valid,false);
-
- if (r_gizmo_handle) {
-
- Transform t = spatial_node->get_global_transform();
- t.orthonormalize();
- if (billboard_handle) {
- t.set_look_at(t.origin,t.origin+p_camera->get_transform().basis.get_axis(2),p_camera->get_transform().basis.get_axis(1));
- }
- Transform ti=t.affine_inverse();
-
- Vector3 ray_from=ti.xform(p_camera->project_ray_origin(p_point));
- Vector3 ray_dir=t.basis.xform_inv(p_camera->project_ray_normal(p_point)).normalized();
- Vector3 ray_to = ray_from+ray_dir*4096;
-
- float min_d=1e20;
- int idx=-1;
-
- for(int i=0;i<secondary_handles.size();i++) {
-#if 1
-
-
- Vector3 hpos = t.xform(secondary_handles[i]);
- Vector2 p = p_camera->unproject_position(hpos);
- if (p.distance_to(p_point)<SpatialEditorGizmos::singleton->handle_t->get_width()*0.6) {
-
-
- real_t dp = p_camera->get_transform().origin.distance_to(hpos);
- if (dp<min_d) {
-
- r_pos=t.xform(hpos);
- r_normal=p_camera->get_transform().basis.get_axis(2);
- min_d=dp;
- idx=i+handles.size();
-
- }
-
- }
-
-#else
- AABB aabb;
- aabb.pos=Vector3(-1,-1,-1)*HANDLE_HALF_SIZE;
- aabb.size=aabb.pos*-2;
- aabb.pos+=secondary_handles[i];
-
-
- Vector3 rpos,rnorm;
-
- if (aabb.intersects_segment(ray_from,ray_to,&rpos,&rnorm)) {
-
- real_t dp = ray_dir.dot(rpos);
- if (dp<min_d) {
-
- r_pos=t.xform(rpos);
- r_normal=ti.basis.xform_inv(rnorm).normalized();
- min_d=dp;
- idx=i+handles.size();
-
- }
- }
-#endif
- }
-
- if (p_sec_first && idx!=-1) {
-
- *r_gizmo_handle=idx;
- return true;
- }
-
- min_d=1e20;
-
- for(int i=0;i<handles.size();i++) {
-
-#if 1
-
-
- Vector3 hpos = t.xform(handles[i]);
- Vector2 p = p_camera->unproject_position(hpos);
- if (p.distance_to(p_point)<SpatialEditorGizmos::singleton->handle_t->get_width()*0.6) {
-
-
- real_t dp = p_camera->get_transform().origin.distance_to(hpos);
- if (dp<min_d) {
-
- r_pos=t.xform(hpos);
- r_normal=p_camera->get_transform().basis.get_axis(2);
- min_d=dp;
- idx=i;
-
- }
-
- }
-
-#else
-
- AABB aabb;
- aabb.pos=Vector3(-1,-1,-1)*HANDLE_HALF_SIZE;
- aabb.size=aabb.pos*-2;
- aabb.pos+=handles[i];
-
-
- Vector3 rpos,rnorm;
-
- if (aabb.intersects_segment(ray_from,ray_to,&rpos,&rnorm)) {
-
- real_t dp = ray_dir.dot(rpos);
- if (dp<min_d) {
-
- r_pos=t.xform(rpos);
- r_normal=ti.basis.xform_inv(rnorm).normalized();
- min_d=dp;
- idx=i;
-
- }
- }
-#endif
- }
-
- if (idx>=0) {
- *r_gizmo_handle=idx;
- return true;
- }
-
-
- }
-
- if (collision_segments.size()) {
-
- Plane camp(p_camera->get_transform().origin,(-p_camera->get_transform().basis.get_axis(2)).normalized());
-
- int vc=collision_segments.size();
- const Vector3* vptr=collision_segments.ptr();
- Transform t = spatial_node->get_global_transform();
- if (billboard_handle) {
- t.set_look_at(t.origin,t.origin+p_camera->get_transform().basis.get_axis(2),p_camera->get_transform().basis.get_axis(1));
- }
-
- Vector3 cp;
- float cpd=1e20;
-
- for(int i=0;i<vc/2;i++) {
-
-
- Vector3 a=t.xform(vptr[i*2+0]);
- Vector3 b=t.xform(vptr[i*2+1]);
- Vector2 s[2];
- s[0] = p_camera->unproject_position(a);
- s[1] = p_camera->unproject_position(b);
-
-
- Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point,s);
-
- float pd = p.distance_to(p_point);
-
- if (pd<cpd) {
-
-
- float d = s[0].distance_to(s[1]);
- Vector3 tcp;
- if (d>0) {
-
- float d2=s[0].distance_to(p)/d;
- tcp = a+(b-a)*d2;
-
- } else {
- tcp=a;
-
- }
-
- if (camp.distance_to(tcp)<p_camera->get_znear())
- continue;
- cp=tcp;
- cpd=pd;
- }
- }
-
- if (cpd<8) {
-
- r_pos=cp;
- r_normal=-p_camera->project_ray_normal(p_point);
- return true;
- }
-
- return false;
- }
-
-
- if (collision_mesh.is_valid()) {
- Transform gt = spatial_node->get_global_transform();
-
- if (billboard_handle) {
- gt.set_look_at(gt.origin,gt.origin+p_camera->get_transform().basis.get_axis(2),p_camera->get_transform().basis.get_axis(1));
- }
-
- Transform ai=gt.affine_inverse();
- Vector3 ray_from = ai.xform(p_camera->project_ray_origin(p_point));
- Vector3 ray_dir=ai.basis.xform(p_camera->project_ray_normal(p_point)).normalized();
- Vector3 rpos,rnorm;
-
-#if 1
-
-
-
- if (collision_mesh->intersect_ray(ray_from,ray_dir,rpos,rnorm)) {
-
- r_pos=gt.xform(rpos);
- r_normal=gt.basis.xform(rnorm).normalized();
- return true;
- }
-#else
-
- if (collision_mesh->intersect_segment(ray_from,ray_from+ray_dir*4906.0,rpos,rnorm)) {
-
- r_pos=gt.xform(rpos);
- r_normal=gt.basis.xform(rnorm).normalized();
- return true;
- }
-
-#endif
- }
-
- return false;
-
-}
-
-
-
-void SpatialGizmoTool::create() {
-
- ERR_FAIL_COND(!spatial_node);
- ERR_FAIL_COND(valid);
- valid=true;
-
- for(int i=0;i<instances.size();i++) {
-
- instances[i].create_instance(spatial_node);
- }
-
- transform();
-
-}
-
-void SpatialGizmoTool::transform(){
-
- ERR_FAIL_COND(!spatial_node);
- ERR_FAIL_COND(!valid);
- for(int i=0;i<instances.size();i++) {
- VS::get_singleton()->instance_set_transform(instances[i].instance,spatial_node->get_global_transform());
- }
-
-}
-
-
-void SpatialGizmoTool::free(){
-
- ERR_FAIL_COND(!spatial_node);
- ERR_FAIL_COND(!valid);
-
- for(int i=0;i<instances.size();i++) {
-
- if (instances[i].instance.is_valid())
- VS::get_singleton()->free(instances[i].instance);
- instances[i].instance=RID();
- }
-
- valid=false;
-
-
-}
-
-
-
-SpatialGizmoTool::SpatialGizmoTool() {
- valid=false;
- billboard_handle=false;
-
-}
-
-SpatialGizmoTool::~SpatialGizmoTool(){
-
- clear();
-}
-
-Vector3 SpatialGizmoTool::get_handle_pos(int p_idx) const {
-
- ERR_FAIL_INDEX_V(p_idx,handles.size(),Vector3());
-
- return handles[p_idx];
-
-}
-
-//// light gizmo
-
-
-String LightSpatialGizmo::get_handle_name(int p_idx) const {
-
- if (p_idx==0)
- return "Radius";
- else
- return "Aperture";
-}
-
-
-Variant LightSpatialGizmo::get_handle_value(int p_idx) const{
-
- if (p_idx==0)
- return light->get_parameter(Light::PARAM_RADIUS);
- if (p_idx==1)
- return light->get_parameter(Light::PARAM_SPOT_ANGLE);
-
- return Variant();
-}
-
-
-static float _find_closest_angle_to_half_pi_arc(const Vector3& p_from, const Vector3& p_to, float p_arc_radius,const Transform& p_arc_xform) {
-
- //bleh, discrete is simpler
- static const int arc_test_points=64;
- float min_d = 1e20;
- Vector3 min_p;
-
-
- for(int i=0;i<arc_test_points;i++) {
-
- float a = i*Math_PI*0.5/arc_test_points;
- float an = (i+1)*Math_PI*0.5/arc_test_points;
- Vector3 p=Vector3( Math::cos(a), 0, -Math::sin(a) )*p_arc_radius;
- Vector3 n=Vector3( Math::cos(an), 0,- Math::sin(an) )*p_arc_radius;
-
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(p,n,p_from,p_to,ra,rb);
-
- float d = ra.distance_to(rb);
- if (d<min_d) {
- min_d=d;
- min_p=ra;
- }
-
- }
-
- //min_p = p_arc_xform.affine_inverse().xform(min_p);
- float a = Vector2(min_p.x,-min_p.z).atan2();
- return a*180.0/Math_PI;
-}
-
-
-void LightSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point) {
-
- Transform gt = light->get_global_transform();
- gt.orthonormalize();
- Transform gi = gt.affine_inverse();
-
- Vector3 ray_from = p_camera->project_ray_origin(p_point);
- Vector3 ray_dir = p_camera->project_ray_normal(p_point);
-
- Vector3 s[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)};
- if (p_idx==0) {
-
- if (light->cast_to<SpotLight>()) {
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(Vector3(),Vector3(0,0,-4096),s[0],s[1],ra,rb);
-
- float d = -ra.z;
- if (d<0)
- d=0;
-
- light->set_parameter(Light::PARAM_RADIUS,d);
- } else if (light->cast_to<OmniLight>()) {
-
- Plane cp=Plane( gt.origin, p_camera->get_transform().basis.get_axis(2));
-
- Vector3 inters;
- if (cp.intersects_ray(ray_from,ray_dir,&inters)) {
-
- float r = inters.distance_to(gt.origin);
- light->set_parameter(Light::PARAM_RADIUS,r);
- }
-
- }
-
- } else if (p_idx==1) {
-
- float a = _find_closest_angle_to_half_pi_arc(s[0],s[1],light->get_parameter(Light::PARAM_RADIUS),gt);
- light->set_parameter(Light::PARAM_SPOT_ANGLE,CLAMP(a,0.01,89.99));
- }
-}
-
-void LightSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
-
- if (p_cancel) {
-
- light->set_parameter(p_idx==0?Light::PARAM_RADIUS:Light::PARAM_SPOT_ANGLE,p_restore);
-
- } else if (p_idx==0) {
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Light Radius");
- ur->add_do_method(light,"set_parameter",Light::PARAM_RADIUS,light->get_parameter(Light::PARAM_RADIUS));
- ur->add_undo_method(light,"set_parameter",Light::PARAM_RADIUS,p_restore);
- ur->commit_action();
- } else if (p_idx==1) {
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Light Radius");
- ur->add_do_method(light,"set_parameter",Light::PARAM_SPOT_ANGLE,light->get_parameter(Light::PARAM_SPOT_ANGLE));
- ur->add_undo_method(light,"set_parameter",Light::PARAM_SPOT_ANGLE,p_restore);
- ur->commit_action();
-
- }
-}
-
-
-
-void LightSpatialGizmo::redraw() {
-
-
- if (light->cast_to<DirectionalLight>()) {
-
-
-
- const int arrow_points=5;
- Vector3 arrow[arrow_points]={
- Vector3(0,0,2),
- Vector3(1,1,2),
- Vector3(1,1,-1),
- Vector3(2,2,-1),
- Vector3(0,0,-3)
- };
-
- int arrow_sides=4;
-
- Vector<Vector3> lines;
-
-
- for(int i = 0; i < arrow_sides ; i++) {
-
-
- Matrix3 ma(Vector3(0,0,1),Math_PI*2*float(i)/arrow_sides);
- Matrix3 mb(Vector3(0,0,1),Math_PI*2*float(i+1)/arrow_sides);
-
-
- for(int j=1;j<arrow_points-1;j++) {
-
- if (j!=2) {
- lines.push_back(ma.xform(arrow[j]));
- lines.push_back(ma.xform(arrow[j+1]));
- }
- if (j<arrow_points-1) {
- lines.push_back(ma.xform(arrow[j]));
- lines.push_back(mb.xform(arrow[j]));
- }
-
- }
- }
-
- add_lines(lines,SpatialEditorGizmos::singleton->light_material);
- add_collision_segments(lines);
- add_unscaled_billboard(SpatialEditorGizmos::singleton->light_material_directional_icon,0.05);
-
- }
-
- if (light->cast_to<OmniLight>()) {
-
- clear();
-
-
- OmniLight *on = light->cast_to<OmniLight>();
-
- float r = on->get_parameter(Light::PARAM_RADIUS);
-
- Vector<Vector3> points;
-
- for(int i=0;i<=360;i++) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+1);
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
-
- /*points.push_back(Vector3(a.x,0,a.y));
- points.push_back(Vector3(b.x,0,b.y));
- points.push_back(Vector3(0,a.x,a.y));
- points.push_back(Vector3(0,b.x,b.y));*/
- points.push_back(Vector3(a.x,a.y,0));
- points.push_back(Vector3(b.x,b.y,0));
-
- }
-
- add_lines(points,SpatialEditorGizmos::singleton->light_material,true);
- add_collision_segments(points);
-
- add_unscaled_billboard(SpatialEditorGizmos::singleton->light_material_omni_icon,0.05);
-
- Vector<Vector3> handles;
- handles.push_back(Vector3(r,0,0));
- add_handles(handles,true);
-
-
- }
-
-
- if (light->cast_to<SpotLight>()) {
-
- clear();
-
- Vector<Vector3> points;
- SpotLight *on = light->cast_to<SpotLight>();
-
- float r = on->get_parameter(Light::PARAM_RADIUS);
- float w = r*Math::sin(Math::deg2rad(on->get_parameter(Light::PARAM_SPOT_ANGLE)));
- float d = r*Math::cos(Math::deg2rad(on->get_parameter(Light::PARAM_SPOT_ANGLE)));
-
-
-
- for(int i=0;i<360;i++) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+1);
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w;
-
- /*points.push_back(Vector3(a.x,0,a.y));
- points.push_back(Vector3(b.x,0,b.y));
- points.push_back(Vector3(0,a.x,a.y));
- points.push_back(Vector3(0,b.x,b.y));*/
- points.push_back(Vector3(a.x,a.y,-d));
- points.push_back(Vector3(b.x,b.y,-d));
-
- if (i%90==0) {
-
- points.push_back(Vector3(a.x,a.y,-d));
- points.push_back(Vector3());
-
- }
-
-
- }
-
- points.push_back(Vector3(0,0,-r));
- points.push_back(Vector3());
-
- add_lines(points,SpatialEditorGizmos::singleton->light_material);
-
- Vector<Vector3> handles;
- handles.push_back(Vector3(0,0,-r));
-
- Vector<Vector3> collision_segments;
-
- for(int i=0;i<64;i++) {
-
- float ra=i*Math_PI*2.0/64.0;
- float rb=(i+1)*Math_PI*2.0/64.0;
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w;
-
- collision_segments.push_back(Vector3(a.x,a.y,-d));
- collision_segments.push_back(Vector3(b.x,b.y,-d));
-
- if (i%16==0) {
-
- collision_segments.push_back(Vector3(a.x,a.y,-d));
- collision_segments.push_back(Vector3());
-
- }
-
- if (i==16) {
-
- handles.push_back(Vector3(a.x,a.y,-d));
- }
-
- }
-
- collision_segments.push_back(Vector3(0,0,-r));
- collision_segments.push_back(Vector3());
-
-
- add_handles(handles);
- add_collision_segments(collision_segments);
- add_unscaled_billboard(SpatialEditorGizmos::singleton->light_material_omni_icon,0.05);
-
- }
-
-}
-
-LightSpatialGizmo::LightSpatialGizmo(Light* p_light){
-
- light=p_light;
- set_spatial_node(p_light);
-
-}
-
-//////
-
-String CameraSpatialGizmo::get_handle_name(int p_idx) const {
-
- if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) {
- return "FOV";
- } else {
- return "Size";
- }
-}
-Variant CameraSpatialGizmo::get_handle_value(int p_idx) const{
-
- if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) {
- return camera->get_fov();
- } else {
-
- return camera->get_size();
- }
-}
-void CameraSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){
-
- Transform gt = camera->get_global_transform();
- gt.orthonormalize();
- Transform gi = gt.affine_inverse();
-
- Vector3 ray_from = p_camera->project_ray_origin(p_point);
- Vector3 ray_dir = p_camera->project_ray_normal(p_point);
-
- Vector3 s[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)};
-
- if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) {
- Transform gt=camera->get_global_transform();
- float a = _find_closest_angle_to_half_pi_arc(s[0],s[1],1.0,gt);
- camera->set("fov",a);
- } else {
-
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(Vector3(0,0,-1),Vector3(4096,0,-1),s[0],s[1],ra,rb);
- float d = ra.x * 2.0;
- if (d<0)
- d=0;
-
- camera->set("size",d);
- }
-
-}
-void CameraSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
-
- if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) {
-
- if (p_cancel) {
-
- camera->set("fov",p_restore);
- } else {
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Camera FOV");
- ur->add_do_property(camera,"fov",camera->get_fov());
- ur->add_undo_property(camera,"fov",p_restore);
- ur->commit_action();
- }
-
- } else {
-
- if (p_cancel) {
-
- camera->set("size",p_restore);
- } else {
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Camera Size");
- ur->add_do_property(camera,"size",camera->get_size());
- ur->add_undo_property(camera,"size",p_restore);
- ur->commit_action();
- }
-
- }
-
-}
-
-void CameraSpatialGizmo::redraw(){
-
- clear();
-
- Vector<Vector3> lines;
- Vector<Vector3> handles;
-
-
- switch(camera->get_projection()) {
-
- case Camera::PROJECTION_PERSPECTIVE: {
-
- float fov = camera->get_fov();
-
- Vector3 side=Vector3( Math::sin(Math::deg2rad(fov)), 0, -Math::cos(Math::deg2rad(fov)) );
- Vector3 nside=side;
- nside.x=-nside.x;
- Vector3 up=Vector3(0,side.x,0);
-
-
-#define ADD_TRIANGLE( m_a, m_b, m_c)\
-{\
- lines.push_back(m_a);\
- lines.push_back(m_b);\
- lines.push_back(m_b);\
- lines.push_back(m_c);\
- lines.push_back(m_c);\
- lines.push_back(m_a);\
-}
-
- ADD_TRIANGLE( Vector3(), side+up, side-up );
- ADD_TRIANGLE( Vector3(), nside+up, nside-up );
- ADD_TRIANGLE( Vector3(), side+up, nside+up );
- ADD_TRIANGLE( Vector3(), side-up, nside-up );
-
- handles.push_back(side);
- side.x*=0.25;
- nside.x*=0.25;
- Vector3 tup( 0, up.y*3/2,side.z);
- ADD_TRIANGLE( tup, side+up, nside+up );
-
- } break;
- case Camera::PROJECTION_ORTHOGONAL: {
-
-#define ADD_QUAD( m_a, m_b, m_c, m_d)\
-{\
- lines.push_back(m_a);\
- lines.push_back(m_b);\
- lines.push_back(m_b);\
- lines.push_back(m_c);\
- lines.push_back(m_c);\
- lines.push_back(m_d);\
- lines.push_back(m_d);\
- lines.push_back(m_a);\
-}
- float size = camera->get_size();
-
- float hsize=size*0.5;
- Vector3 right(hsize,0,0);
- Vector3 up(0,hsize,0);
- Vector3 back(0,0,-1.0);
- Vector3 front(0,0,0);
-
- ADD_QUAD( -up-right,-up+right,up+right,up-right);
- ADD_QUAD( -up-right+back,-up+right+back,up+right+back,up-right+back);
- ADD_QUAD( up+right,up+right+back,up-right+back,up-right);
- ADD_QUAD( -up+right,-up+right+back,-up-right+back,-up-right);
- handles.push_back(right+back);
-
- right.x*=0.25;
- Vector3 tup( 0, up.y*3/2,back.z );
- ADD_TRIANGLE( tup, right+up+back, -right+up+back );
-
- } break;
-
- }
-
- add_lines(lines,SpatialEditorGizmos::singleton->camera_material);
- add_collision_segments(lines);
- add_handles(handles);
-}
-
-
-CameraSpatialGizmo::CameraSpatialGizmo(Camera* p_camera){
-
- camera=p_camera;
- set_spatial_node(camera);
-}
-
-
-
-
-//////
-
-void MeshInstanceSpatialGizmo::redraw() {
-
- Ref<Mesh> m = mesh->get_mesh();
- if (!m.is_valid())
- return; //none
-
- Ref<TriangleMesh> tm = m->generate_triangle_mesh();
- if (tm.is_valid())
- add_collision_triangles(tm);
-}
-
-MeshInstanceSpatialGizmo::MeshInstanceSpatialGizmo(MeshInstance* p_mesh) {
-
- mesh=p_mesh;
- set_spatial_node(p_mesh);
-}
-
-/////
-
-
-void Position3DSpatialGizmo::redraw() {
-
- clear();
- add_mesh(SpatialEditorGizmos::singleton->pos3d_mesh);
- Vector<Vector3> cursor_points;
- float cs = 0.25;
- cursor_points.push_back(Vector3(+cs,0,0));
- cursor_points.push_back(Vector3(-cs,0,0));
- cursor_points.push_back(Vector3(0,+cs,0));
- cursor_points.push_back(Vector3(0,-cs,0));
- cursor_points.push_back(Vector3(0,0,+cs));
- cursor_points.push_back(Vector3(0,0,-cs));
- add_collision_segments(cursor_points);
-
-}
-
-
-Position3DSpatialGizmo::Position3DSpatialGizmo(Position3D* p_p3d) {
-
- p3d=p_p3d;
- set_spatial_node(p3d);
-}
-
-
-/////
-
-void SkeletonSpatialGizmo::redraw() {
-
- clear();
-
- Ref<SurfaceTool> surface_tool( memnew( SurfaceTool ));
-
-
- surface_tool->begin(Mesh::PRIMITIVE_LINES);
- surface_tool->set_material(SpatialEditorGizmos::singleton->skeleton_material);
- Vector<Transform> grests;
- grests.resize(skel->get_bone_count());
-
- Vector<int> bones;
- Vector<float> weights;
- bones.resize(4);
- weights.resize(4);
-
- for(int i=0;i<4;i++) {
- bones[i]=0;
- weights[i]=0;
- }
-
- weights[0]=1;
-
-
- AABB aabb;
-
- Color bonecolor = Color(1.0,0.4,0.4,0.3);
- Color rootcolor = Color(0.4,1.0,0.4,0.1);
-
- for (int i=0;i<skel->get_bone_count();i++) {
-
- int parent = skel->get_bone_parent(i);
-
- if (parent>=0) {
- grests[i]=grests[parent] * skel->get_bone_rest(i);
-
- Vector3 v0 = grests[parent].origin;
- Vector3 v1 = grests[i].origin;
- Vector3 d = (v1-v0).normalized();
- float dist = v0.distance_to(v1);
-
- //find closest axis
- int closest=-1;
- float closest_d;
- for(int j=0;j<3;j++) {
- float dp = Math::abs(grests[parent].basis[j].normalized().dot(d));
- if (j==0 || dp>closest_d)
- closest=j;
- }
-
- //find closest other
- Vector3 first;
- Vector3 points[4];
- int pointidx=0;
- for(int j=0;j<3;j++) {
-
- bones[0]=parent;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(rootcolor);
- surface_tool->add_vertex(v0-grests[parent].basis[j].normalized()*dist*0.05);
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(rootcolor);
- surface_tool->add_vertex(v0+grests[parent].basis[j].normalized()*dist*0.05);
-
- if (j==closest)
- continue;
-
- Vector3 axis;
- if (first==Vector3()) {
- axis = d.cross(d.cross(grests[parent].basis[j])).normalized();
- first=axis;
- } else {
- axis = d.cross(first).normalized();
- }
-
- for(int k=0;k<2;k++) {
-
- if (k==1)
- axis=-axis;
- Vector3 point = v0+d*dist*0.2;
- point+=axis*dist*0.1;
-
-
- bones[0]=parent;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
- surface_tool->add_vertex(v0);
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
- surface_tool->add_vertex(point);
-
- bones[0]=parent;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
- surface_tool->add_vertex(point);
- bones[0]=i;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
- surface_tool->add_vertex(v1);
- points[pointidx++]=point;
-
- }
-
- }
-
- SWAP( points[1],points[2] );
- for(int j=0;j<4;j++) {
-
-
- bones[0]=parent;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
- surface_tool->add_vertex(points[j]);
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
- surface_tool->add_vertex(points[(j+1)%4]);
- }
-
-
-/*
- bones[0]=parent;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(Color(0.4,1,0.4,0.4));
- surface_tool->add_vertex(v0);
- bones[0]=i;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(Color(0.4,1,0.4,0.4));
- surface_tool->add_vertex(v1);
-*/
- } else {
-
- grests[i]=skel->get_bone_rest(i);
- bones[0]=i;
- }
-/*
- Transform t = grests[i];
- t.orthonormalize();
-
- for (int i=0;i<6;i++) {
-
-
- Vector3 face_points[4];
-
- for (int j=0;j<4;j++) {
-
- float v[3];
- v[0]=1.0;
- v[1]=1-2*((j>>1)&1);
- v[2]=v[1]*(1-2*(j&1));
-
- for (int k=0;k<3;k++) {
-
- if (i<3)
- face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
- else
- face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
- }
- }
-
- for(int j=0;j<4;j++) {
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(Color(1.0,0.4,0.4,0.4));
- surface_tool->add_vertex(t.xform(face_points[j]*0.04));
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(Color(1.0,0.4,0.4,0.4));
- surface_tool->add_vertex(t.xform(face_points[(j+1)%4]*0.04));
- }
-
- }
- */
- }
-
- Ref<Mesh> m = surface_tool->commit();
- add_mesh(m,false,skel->get_skeleton());
-
-}
-
-SkeletonSpatialGizmo::SkeletonSpatialGizmo(Skeleton* p_skel) {
-
- skel=p_skel;
- set_spatial_node(p_skel);
-}
-
-/////
-
-
-void SpatialPlayerSpatialGizmo::redraw() {
-
- clear();
- if (splayer->cast_to<SpatialStreamPlayer>()) {
-
- add_unscaled_billboard(SpatialEditorGizmos::singleton->stream_player_icon,0.05);
-
- } else if (splayer->cast_to<SpatialSamplePlayer>()) {
-
- add_unscaled_billboard(SpatialEditorGizmos::singleton->sample_player_icon,0.05);
-
- }
-
-}
-
-SpatialPlayerSpatialGizmo::SpatialPlayerSpatialGizmo(SpatialPlayer* p_splayer){
-
- set_spatial_node(p_splayer);
- splayer=p_splayer;
-}
-
-
-/////
-
-
-void RoomSpatialGizmo::redraw() {
-
- clear();
- Ref<RoomBounds> roomie = room->get_room();
- if (roomie.is_null())
- return;
- DVector<Face3> faces = roomie->get_geometry_hint();
-
- Vector<Vector3> lines;
- int fc=faces.size();
- DVector<Face3>::Read r =faces.read();
-
- Map<_EdgeKey,Vector3> edge_map;
-
- for(int i=0;i<fc;i++) {
-
- Vector3 fn = r[i].get_plane().normal;
-
- for(int j=0;j<3;j++) {
-
- _EdgeKey ek;
- ek.from=r[i].vertex[j].snapped(CMP_EPSILON);
- ek.to=r[i].vertex[(j+1)%3].snapped(CMP_EPSILON);
- if (ek.from<ek.to)
- SWAP(ek.from,ek.to);
-
- Map<_EdgeKey,Vector3>::Element *E=edge_map.find(ek);
-
- if (E) {
-
- if (E->get().dot(fn) >0.9) {
-
- E->get()=Vector3();
- }
-
- } else {
-
- edge_map[ek]=fn;
- }
-
- }
- }
-
- for(Map<_EdgeKey,Vector3>::Element *E=edge_map.front();E;E=E->next()) {
-
- if (E->get()!=Vector3()) {
- lines.push_back(E->key().from);
- lines.push_back(E->key().to);
- }
- }
-
- add_lines(lines,SpatialEditorGizmos::singleton->room_material);
- add_collision_segments(lines);
-
-}
-
-RoomSpatialGizmo::RoomSpatialGizmo(Room* p_room){
-
- set_spatial_node(p_room);
- room=p_room;
-}
-
-/////
-
-
-void PortalSpatialGizmo::redraw() {
-
- clear();
-
- Vector<Point2> points = portal->get_shape();
- if (points.size()==0) {
- return;
- }
-
- Vector<Vector3> lines;
-
- Vector3 center;
- for(int i=0;i<points.size();i++) {
-
- Vector3 f;
- f.x=points[i].x;
- f.y=points[i].y;
- Vector3 fn;
- fn.x=points[(i+1)%points.size()].x;
- fn.y=points[(i+1)%points.size()].y;
- center+=f;
-
- lines.push_back(f);
- lines.push_back(fn);
- }
-
- center/=points.size();
- lines.push_back(center);
- lines.push_back(center+Vector3(0,0,1));
-
- add_lines(lines,SpatialEditorGizmos::singleton->portal_material);
- add_collision_segments(lines);
-
-}
-
-PortalSpatialGizmo::PortalSpatialGizmo(Portal* p_portal){
-
- set_spatial_node(p_portal);
- portal=p_portal;
-}
-
-/////
-
-
-void RayCastSpatialGizmo::redraw() {
-
- clear();
-
-
- Vector<Vector3> lines;
-
- lines.push_back(Vector3());
- lines.push_back(raycast->get_cast_to());
-
- add_lines(lines,SpatialEditorGizmos::singleton->raycast_material);
- add_collision_segments(lines);
-
-}
-
-RayCastSpatialGizmo::RayCastSpatialGizmo(RayCast* p_raycast){
-
- set_spatial_node(p_raycast);
- raycast=p_raycast;
-}
-
-
-
-/////
-
-
-void VehicleWheelSpatialGizmo::redraw() {
-
- clear();
-
-
- Vector<Vector3> points;
-
- float r = car_wheel->get_radius();
- const int skip=10;
- for(int i=0;i<=360;i+=skip) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+skip);
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
-
- points.push_back(Vector3(0,a.x,a.y));
- points.push_back(Vector3(0,b.x,b.y));
-
- const int springsec=4;
-
- for(int j=0;j<springsec;j++) {
- float t = car_wheel->get_suspension_rest_length()*5;
- points.push_back(Vector3(a.x,i/360.0*t/springsec+j*(t/springsec),a.y)*0.2);
- points.push_back(Vector3(b.x,(i+skip)/360.0*t/springsec+j*(t/springsec),b.y)*0.2);
- }
-
-
- }
-
- //travel
- points.push_back(Vector3(0,0,0));
- points.push_back(Vector3(0,car_wheel->get_suspension_rest_length(),0));
-
- //axis
- points.push_back(Vector3(r*0.2,car_wheel->get_suspension_rest_length(),0));
- points.push_back(Vector3(-r*0.2,car_wheel->get_suspension_rest_length(),0));
- //axis
- points.push_back(Vector3(r*0.2,0,0));
- points.push_back(Vector3(-r*0.2,0,0));
-
- //forward line
- points.push_back(Vector3(0,-r,0));
- points.push_back(Vector3(0,-r,r*2));
- points.push_back(Vector3(0,-r,r*2));
- points.push_back(Vector3(r*2*0.2,-r,r*2*0.8));
- points.push_back(Vector3(0,-r,r*2));
- points.push_back(Vector3(-r*2*0.2,-r,r*2*0.8));
-
- add_lines(points,SpatialEditorGizmos::singleton->car_wheel_material);
- add_collision_segments(points);
-
-}
-
-VehicleWheelSpatialGizmo::VehicleWheelSpatialGizmo(VehicleWheel* p_car_wheel){
-
- set_spatial_node(p_car_wheel);
- car_wheel=p_car_wheel;
-}
-
-
-
-///
-
-void TestCubeSpatialGizmo::redraw() {
-
- clear();
- add_collision_triangles(SpatialEditorGizmos::singleton->test_cube_tm);
-}
-
-TestCubeSpatialGizmo::TestCubeSpatialGizmo(TestCube* p_tc) {
-
- tc=p_tc;
- set_spatial_node(p_tc);
-}
-
-
-///////////
-
-
-
-
-
-
-String CollisionShapeSpatialGizmo::get_handle_name(int p_idx) const {
-
- Ref<Shape> s = cs->get_shape();
- if (s.is_null())
- return "";
-
- if (s->cast_to<SphereShape>()) {
-
- return "Radius";
- }
-
- if (s->cast_to<BoxShape>()) {
-
- return "Extents";
- }
-
- if (s->cast_to<CapsuleShape>()) {
-
- return p_idx==0?"Radius":"Height";
- }
-
- if (s->cast_to<RayShape>()) {
-
- return "Length";
- }
-
- return "";
-}
-Variant CollisionShapeSpatialGizmo::get_handle_value(int p_idx) const{
-
- Ref<Shape> s = cs->get_shape();
- if (s.is_null())
- return Variant();
-
- if (s->cast_to<SphereShape>()) {
-
- Ref<SphereShape> ss = s;
- return ss->get_radius();
- }
-
- if (s->cast_to<BoxShape>()) {
-
- Ref<BoxShape> bs = s;
- return bs->get_extents();
- }
-
- if (s->cast_to<CapsuleShape>()) {
-
- Ref<CapsuleShape> cs = s;
- return p_idx==0?cs->get_radius():cs->get_height();
- }
-
- if (s->cast_to<RayShape>()) {
-
- Ref<RayShape> cs = s;
- return cs->get_length();
- }
-
- return Variant();
-}
-void CollisionShapeSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){
- Ref<Shape> s = cs->get_shape();
- if (s.is_null())
- return;
-
- Transform gt = cs->get_global_transform();
- gt.orthonormalize();
- Transform gi = gt.affine_inverse();
-
- Vector3 ray_from = p_camera->project_ray_origin(p_point);
- Vector3 ray_dir = p_camera->project_ray_normal(p_point);
-
- Vector3 sg[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)};
-
- if (s->cast_to<SphereShape>()) {
-
- Ref<SphereShape> ss = s;
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(Vector3(),Vector3(4096,0,0),sg[0],sg[1],ra,rb);
- float d = ra.x;
- if (d<0.001)
- d=0.001;
-
- ss->set_radius(d);
- }
-
- if (s->cast_to<RayShape>()) {
-
- Ref<RayShape> rs = s;
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(Vector3(),Vector3(0,0,4096),sg[0],sg[1],ra,rb);
- float d = ra.z;
- if (d<0.001)
- d=0.001;
-
- rs->set_length(d);
- }
-
-
- if (s->cast_to<BoxShape>()) {
-
- Vector3 axis;
- axis[p_idx]=1.0;
- Ref<BoxShape> bs = s;
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(Vector3(),axis*4096,sg[0],sg[1],ra,rb);
- float d = ra[p_idx];
- if (d<0.001)
- d=0.001;
-
- Vector3 he = bs->get_extents();
- he[p_idx]=d;
- bs->set_extents(he);
-
- }
-
- if (s->cast_to<CapsuleShape>()) {
-
- Vector3 axis;
- axis[p_idx==0?0:2]=1.0;
- Ref<CapsuleShape> cs = s;
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(Vector3(),axis*4096,sg[0],sg[1],ra,rb);
- float d = axis.dot(ra);
- if (p_idx==1)
- d-=cs->get_radius();
- if (d<0.001)
- d=0.001;
-
- if (p_idx==0)
- cs->set_radius(d);
- else if (p_idx==1)
- cs->set_height(d*2.0);
-
- }
-
-}
-void CollisionShapeSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
- Ref<Shape> s = cs->get_shape();
- if (s.is_null())
- return;
-
- if (s->cast_to<SphereShape>()) {
-
- Ref<SphereShape> ss=s;
- if (p_cancel) {
- ss->set_radius(p_restore);
- return;
- }
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Sphere Shape Radius");
- ur->add_do_method(ss.ptr(),"set_radius",ss->get_radius());
- ur->add_undo_method(ss.ptr(),"set_radius",p_restore);
- ur->commit_action();
-
- }
-
- if (s->cast_to<BoxShape>()) {
-
- Ref<BoxShape> ss=s;
- if (p_cancel) {
- ss->set_extents(p_restore);
- return;
- }
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Box Shape Extents");
- ur->add_do_method(ss.ptr(),"set_extents",ss->get_extents());
- ur->add_undo_method(ss.ptr(),"set_extents",p_restore);
- ur->commit_action();
- }
-
- if (s->cast_to<CapsuleShape>()) {
-
- Ref<CapsuleShape> ss=s;
- if (p_cancel) {
- if (p_idx==0)
- ss->set_radius(p_restore);
- else
- ss->set_height(p_restore);
- return;
- }
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- if (p_idx==0) {
- ur->create_action("Change Capsule Shape Radius");
- ur->add_do_method(ss.ptr(),"set_radius",ss->get_radius());
- ur->add_undo_method(ss.ptr(),"set_radius",p_restore);
- } else {
- ur->create_action("Change Capsule Shape Height");
- ur->add_do_method(ss.ptr(),"set_height",ss->get_height());
- ur->add_undo_method(ss.ptr(),"set_height",p_restore);
-
- }
-
- ur->commit_action();
-
- }
-
- if (s->cast_to<RayShape>()) {
-
- Ref<RayShape> ss=s;
- if (p_cancel) {
- ss->set_length(p_restore);
- return;
- }
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Ray Shape Length");
- ur->add_do_method(ss.ptr(),"set_length",ss->get_length());
- ur->add_undo_method(ss.ptr(),"set_length",p_restore);
- ur->commit_action();
-
- }
-
-}
-void CollisionShapeSpatialGizmo::redraw(){
-
- clear();
-
- Ref<Shape> s = cs->get_shape();
- if (s.is_null())
- return;
-
- if (s->cast_to<SphereShape>()) {
-
- Ref<SphereShape> sp= s;
- float r=sp->get_radius();
-
- Vector<Vector3> points;
-
- for(int i=0;i<=360;i++) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+1);
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
-
- points.push_back(Vector3(a.x,0,a.y));
- points.push_back(Vector3(b.x,0,b.y));
- points.push_back(Vector3(0,a.x,a.y));
- points.push_back(Vector3(0,b.x,b.y));
- points.push_back(Vector3(a.x,a.y,0));
- points.push_back(Vector3(b.x,b.y,0));
-
- }
-
- Vector<Vector3> collision_segments;
-
- for(int i=0;i<64;i++) {
-
- float ra=i*Math_PI*2.0/64.0;
- float rb=(i+1)*Math_PI*2.0/64.0;
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
-
- collision_segments.push_back(Vector3(a.x,0,a.y));
- collision_segments.push_back(Vector3(b.x,0,b.y));
- collision_segments.push_back(Vector3(0,a.x,a.y));
- collision_segments.push_back(Vector3(0,b.x,b.y));
- collision_segments.push_back(Vector3(a.x,a.y,0));
- collision_segments.push_back(Vector3(b.x,b.y,0));
- }
-
- add_lines(points,SpatialEditorGizmos::singleton->shape_material);
- add_collision_segments(collision_segments);
- Vector<Vector3> handles;
- handles.push_back(Vector3(r,0,0));
- add_handles(handles);
-
- }
-
- if (s->cast_to<BoxShape>()) {
-
- Ref<BoxShape> bs=s;
- Vector<Vector3> lines;
- AABB aabb;
- aabb.pos=-bs->get_extents();
- aabb.size=aabb.pos*-2;
-
- for(int i=0;i<12;i++) {
- Vector3 a,b;
- aabb.get_edge(i,a,b);
- lines.push_back(a);
- lines.push_back(b);
- }
-
- Vector<Vector3> handles;
-
- for(int i=0;i<3;i++) {
-
- Vector3 ax;
- ax[i]=bs->get_extents()[i];
- handles.push_back(ax);
- }
-
- add_lines(lines,SpatialEditorGizmos::singleton->shape_material);
- add_collision_segments(lines);
- add_handles(handles);
-
- }
-
- if (s->cast_to<CapsuleShape>()) {
-
- Ref<CapsuleShape> cs=s;
- float radius = cs->get_radius();
- float height = cs->get_height();
-
-
- Vector<Vector3> points;
-
- Vector3 d(0,0,height*0.5);
- for(int i=0;i<360;i++) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+1);
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*radius;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*radius;
-
- points.push_back(Vector3(a.x,a.y,0)+d);
- points.push_back(Vector3(b.x,b.y,0)+d);
-
- points.push_back(Vector3(a.x,a.y,0)-d);
- points.push_back(Vector3(b.x,b.y,0)-d);
-
- if (i%90==0) {
-
- points.push_back(Vector3(a.x,a.y,0)+d);
- points.push_back(Vector3(a.x,a.y,0)-d);
- }
-
- Vector3 dud = i<180?d:-d;
-
- points.push_back(Vector3(0,a.y,a.x)+dud);
- points.push_back(Vector3(0,b.y,b.x)+dud);
- points.push_back(Vector3(a.y,0,a.x)+dud);
- points.push_back(Vector3(b.y,0,b.x)+dud);
-
- }
-
- add_lines(points,SpatialEditorGizmos::singleton->shape_material);
-
- Vector<Vector3> collision_segments;
-
- for(int i=0;i<64;i++) {
-
- float ra=i*Math_PI*2.0/64.0;
- float rb=(i+1)*Math_PI*2.0/64.0;
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*radius;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*radius;
-
- collision_segments.push_back(Vector3(a.x,a.y,0)+d);
- collision_segments.push_back(Vector3(b.x,b.y,0)+d);
-
- collision_segments.push_back(Vector3(a.x,a.y,0)-d);
- collision_segments.push_back(Vector3(b.x,b.y,0)-d);
-
- if (i%16==0) {
-
- collision_segments.push_back(Vector3(a.x,a.y,0)+d);
- collision_segments.push_back(Vector3(a.x,a.y,0)-d);
- }
-
- Vector3 dud = i<32?d:-d;
-
- collision_segments.push_back(Vector3(0,a.y,a.x)+dud);
- collision_segments.push_back(Vector3(0,b.y,b.x)+dud);
- collision_segments.push_back(Vector3(a.y,0,a.x)+dud);
- collision_segments.push_back(Vector3(b.y,0,b.x)+dud);
-
- }
-
- add_collision_segments(collision_segments);
-
- Vector<Vector3> handles;
- handles.push_back(Vector3(cs->get_radius(),0,0));
- handles.push_back(Vector3(0,0,cs->get_height()*0.5+cs->get_radius()));
- add_handles(handles);
-
-
- }
-
- if (s->cast_to<PlaneShape>()) {
-
- Ref<PlaneShape> ps=s;
- Plane p = ps->get_plane();
- Vector<Vector3> points;
-
- Vector3 n1 = p.get_any_perpendicular_normal();
- Vector3 n2 = p.normal.cross(n1).normalized();
-
- Vector3 pface[4]={
- p.normal*p.d+n1*10.0+n2*10.0,
- p.normal*p.d+n1*10.0+n2*-10.0,
- p.normal*p.d+n1*-10.0+n2*-10.0,
- p.normal*p.d+n1*-10.0+n2*10.0,
- };
-
- points.push_back(pface[0]);
- points.push_back(pface[1]);
- points.push_back(pface[1]);
- points.push_back(pface[2]);
- points.push_back(pface[2]);
- points.push_back(pface[3]);
- points.push_back(pface[3]);
- points.push_back(pface[0]);
- points.push_back(p.normal*p.d);
- points.push_back(p.normal*p.d+p.normal*3);
-
- add_lines(points,SpatialEditorGizmos::singleton->shape_material);
- add_collision_segments(points);
-
- }
-
-
- if (s->cast_to<ConvexPolygonShape>()) {
-
- DVector<Vector3> points = s->cast_to<ConvexPolygonShape>()->get_points();
-
- if (points.size()>3) {
-
- QuickHull qh;
- Vector<Vector3> varr = Variant(points);
- Geometry::MeshData md;
- Error err = qh.build(varr,md);
- if (err==OK) {
- Vector<Vector3> points;
- points.resize(md.edges.size()*2);
- for(int i=0;i<md.edges.size();i++) {
- points[i*2+0]=md.vertices[md.edges[i].a];
- points[i*2+1]=md.vertices[md.edges[i].b];
- }
-
-
- add_lines(points,SpatialEditorGizmos::singleton->shape_material);
- add_collision_segments(points);
-
- }
- }
-
- }
-
-
- if (s->cast_to<RayShape>()) {
-
- Ref<RayShape> rs=s;
-
- Vector<Vector3> points;
- points.push_back(Vector3());
- points.push_back(Vector3(0,0,rs->get_length()));
- add_lines(points,SpatialEditorGizmos::singleton->shape_material);
- add_collision_segments(points);
- Vector<Vector3> handles;
- handles.push_back(Vector3(0,0,rs->get_length()));
- add_handles(handles);
-
-
- }
-
-}
-CollisionShapeSpatialGizmo::CollisionShapeSpatialGizmo(CollisionShape* p_cs) {
-
- cs=p_cs;
- set_spatial_node(p_cs);
-}
-
-
-
-/////
-
-
-void CollisionPolygonSpatialGizmo::redraw() {
-
- clear();
-
- Vector<Vector2> points = polygon->get_polygon();
- float depth = polygon->get_depth()*0.5;
-
- Vector<Vector3> lines;
- for(int i=0;i<points.size();i++) {
-
- int n = (i+1)%points.size();
- lines.push_back(Vector3(points[i].x,points[i].y,depth));
- lines.push_back(Vector3(points[n].x,points[n].y,depth));
- lines.push_back(Vector3(points[i].x,points[i].y,-depth));
- lines.push_back(Vector3(points[n].x,points[n].y,-depth));
- lines.push_back(Vector3(points[i].x,points[i].y,depth));
- lines.push_back(Vector3(points[i].x,points[i].y,-depth));
-
- }
-
- add_lines(lines,SpatialEditorGizmos::singleton->shape_material);
- add_collision_segments(lines);
-}
-
-CollisionPolygonSpatialGizmo::CollisionPolygonSpatialGizmo(CollisionPolygon* p_polygon){
-
- set_spatial_node(p_polygon);
- polygon=p_polygon;
-}
-///
-
-
-String VisibilityNotifierGizmo::get_handle_name(int p_idx) const {
-
- switch(p_idx) {
- case 0: return "X";
- case 1: return "Y";
- case 2: return "Z";
- }
-
- return "";
-}
-Variant VisibilityNotifierGizmo::get_handle_value(int p_idx) const{
-
- return notifier->get_aabb();
-}
-void VisibilityNotifierGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){
-
-
- Transform gt = notifier->get_global_transform();
- //gt.orthonormalize();
- Transform gi = gt.affine_inverse();
-
- AABB aabb = notifier->get_aabb();
- Vector3 ray_from = p_camera->project_ray_origin(p_point);
- Vector3 ray_dir = p_camera->project_ray_normal(p_point);
-
- Vector3 sg[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)};
- Vector3 ofs = aabb.pos+aabb.size*0.5;;
-
- Vector3 axis;
- axis[p_idx]=1.0;
-
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(ofs,ofs+axis*4096,sg[0],sg[1],ra,rb);
- float d = ra[p_idx];
- if (d<0.001)
- d=0.001;
-
- Vector3 he = aabb.size;
- aabb.pos[p_idx]=(aabb.pos[p_idx]+aabb.size[p_idx]*0.5)-d;
- aabb.size[p_idx]=d*2;
- notifier->set_aabb(aabb);
-}
-
-void VisibilityNotifierGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
-
-
- if (p_cancel) {
- notifier->set_aabb(p_restore);
- return;
- }
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Notifier Extents");
- ur->add_do_method(notifier,"set_aabb",notifier->get_aabb());
- ur->add_undo_method(notifier,"set_aabb",p_restore);
- ur->commit_action();
-
-}
-
-void VisibilityNotifierGizmo::redraw(){
-
- clear();
-
- Vector<Vector3> lines;
- AABB aabb = notifier->get_aabb();
-
- for(int i=0;i<12;i++) {
- Vector3 a,b;
- aabb.get_edge(i,a,b);
- lines.push_back(a);
- lines.push_back(b);
- }
-
- Vector<Vector3> handles;
-
-
- for(int i=0;i<3;i++) {
-
- Vector3 ax;
- ax[i]=aabb.pos[i]+aabb.size[i];
- handles.push_back(ax);
- }
-
- add_lines(lines,SpatialEditorGizmos::singleton->visibility_notifier_material);
- //add_unscaled_billboard(SpatialEditorGizmos::singleton->visi,0.05);
- add_collision_segments(lines);
- add_handles(handles);
-
-}
-VisibilityNotifierGizmo::VisibilityNotifierGizmo(VisibilityNotifier* p_notifier){
-
- notifier=p_notifier;
- set_spatial_node(p_notifier);
-}
-
-////////
-
-
-
-void NavigationMeshSpatialGizmo::redraw() {
-
- clear();
- Ref<NavigationMesh> navmeshie = navmesh->get_navigation_mesh();
- if (navmeshie.is_null())
- return;
-
- DVector<Vector3> vertices = navmeshie->get_vertices();
- DVector<Vector3>::Read vr=vertices.read();
- List<Face3> faces;
- for(int i=0;i<navmeshie->get_polygon_count();i++) {
- Vector<int> p = navmeshie->get_polygon(i);
-
- for(int j=2;j<p.size();j++) {
- Face3 f;
- f.vertex[0]=vr[p[0]];
- f.vertex[1]=vr[p[j-1]];
- f.vertex[2]=vr[p[j]];
-
- faces.push_back(f);
- }
- }
-
-
- Map<_EdgeKey,bool> edge_map;
- DVector<Vector3> tmeshfaces;
- tmeshfaces.resize(faces.size()*3);
-
- {
- DVector<Vector3>::Write tw=tmeshfaces.write();
- int tidx=0;
-
-
- for(List<Face3>::Element *E=faces.front();E;E=E->next()) {
-
- const Face3 &f = E->get();
-
- for(int j=0;j<3;j++) {
-
- tw[tidx++]=f.vertex[j];
- _EdgeKey ek;
- ek.from=f.vertex[j].snapped(CMP_EPSILON);
- ek.to=f.vertex[(j+1)%3].snapped(CMP_EPSILON);
- if (ek.from<ek.to)
- SWAP(ek.from,ek.to);
-
- Map<_EdgeKey,bool>::Element *E=edge_map.find(ek);
-
- if (E) {
-
- E->get()=false;
-
- } else {
-
- edge_map[ek]=true;
- }
-
- }
- }
- }
- Vector<Vector3> lines;
-
- for(Map<_EdgeKey,bool>::Element *E=edge_map.front();E;E=E->next()) {
-
- if (E->get()) {
- lines.push_back(E->key().from);
- lines.push_back(E->key().to);
- }
- }
-
- Ref<TriangleMesh> tmesh = memnew( TriangleMesh);
- tmesh->create(tmeshfaces);
-
- if (lines.size())
- add_lines(lines,navmesh->is_enabled()?SpatialEditorGizmos::singleton->navmesh_edge_material:SpatialEditorGizmos::singleton->navmesh_edge_material_disabled);
- add_collision_triangles(tmesh);
- Ref<Mesh> m = memnew( Mesh );
- Array a;
- a.resize(Mesh::ARRAY_MAX);
- a[0]=tmeshfaces;
- m->add_surface(Mesh::PRIMITIVE_TRIANGLES,a);
- m->surface_set_material(0,navmesh->is_enabled()?SpatialEditorGizmos::singleton->navmesh_solid_material:SpatialEditorGizmos::singleton->navmesh_solid_material_disabled);
- add_mesh(m);
- add_collision_segments(lines);
-
-}
-
-NavigationMeshSpatialGizmo::NavigationMeshSpatialGizmo(NavigationMeshInstance *p_navmesh){
-
- set_spatial_node(p_navmesh);
- navmesh=p_navmesh;
-}
-
-//////
-///
-///
-
-
-
-void PinJointSpatialGizmo::redraw() {
-
- clear();
- Vector<Vector3> cursor_points;
- float cs = 0.25;
- cursor_points.push_back(Vector3(+cs,0,0));
- cursor_points.push_back(Vector3(-cs,0,0));
- cursor_points.push_back(Vector3(0,+cs,0));
- cursor_points.push_back(Vector3(0,-cs,0));
- cursor_points.push_back(Vector3(0,0,+cs));
- cursor_points.push_back(Vector3(0,0,-cs));
- add_collision_segments(cursor_points);
- add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
-
-}
-
-
-PinJointSpatialGizmo::PinJointSpatialGizmo(PinJoint* p_p3d) {
-
- p3d=p_p3d;
- set_spatial_node(p3d);
-}
-
-////
-
-void HingeJointSpatialGizmo::redraw() {
-
- clear();
- Vector<Vector3> cursor_points;
- float cs = 0.25;
- /*cursor_points.push_back(Vector3(+cs,0,0));
- cursor_points.push_back(Vector3(-cs,0,0));
- cursor_points.push_back(Vector3(0,+cs,0));
- cursor_points.push_back(Vector3(0,-cs,0));*/
- cursor_points.push_back(Vector3(0,0,+cs*2));
- cursor_points.push_back(Vector3(0,0,-cs*2));
-
- float ll = p3d->get_param(HingeJoint::PARAM_LIMIT_LOWER);
- float ul = p3d->get_param(HingeJoint::PARAM_LIMIT_UPPER);
-
- if (p3d->get_flag(HingeJoint::FLAG_USE_LIMIT) && ll<ul) {
-
- const int points = 32;
- float step = (ul-ll)/points;
-
-
- for(int i=0;i<points;i++) {
-
- float s = ll+i*(ul-ll)/points;
- float n = ll+(i+1)*(ul-ll)/points;
-
- Vector3 from=Vector3( -Math::sin(s),Math::cos(s), 0 )*cs;
- Vector3 to=Vector3( -Math::sin(n),Math::cos(n), 0 )*cs;
-
- if (i==points-1) {
- cursor_points.push_back(to);
- cursor_points.push_back(Vector3());
- }
- if (i==0) {
- cursor_points.push_back(from);
- cursor_points.push_back(Vector3());
- }
-
-
- cursor_points.push_back(from);
- cursor_points.push_back(to);
-
-
- }
-
- cursor_points.push_back(Vector3(0,cs*1.5,0));
- cursor_points.push_back(Vector3());
-
- } else {
-
-
- const int points = 32;
-
- for(int i=0;i<points;i++) {
-
- float s = ll+i*(Math_PI*2.0)/points;
- float n = ll+(i+1)*(Math_PI*2.0)/points;
-
- Vector3 from=Vector3( -Math::sin(s),Math::cos(s), 0 )*cs;
- Vector3 to=Vector3( -Math::sin(n),Math::cos(n), 0 )*cs;
-
- cursor_points.push_back(from);
- cursor_points.push_back(to);
-
- }
-
- }
- add_collision_segments(cursor_points);
- add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
-
-}
-
-
-HingeJointSpatialGizmo::HingeJointSpatialGizmo(HingeJoint* p_p3d) {
-
- p3d=p_p3d;
- set_spatial_node(p3d);
-}
-
-///////
-///
-////
-
-void SliderJointSpatialGizmo::redraw() {
-
- clear();
- Vector<Vector3> cursor_points;
- float cs = 0.25;
- /*cursor_points.push_back(Vector3(+cs,0,0));
- cursor_points.push_back(Vector3(-cs,0,0));
- cursor_points.push_back(Vector3(0,+cs,0));
- cursor_points.push_back(Vector3(0,-cs,0));*/
- cursor_points.push_back(Vector3(0,0,+cs*2));
- cursor_points.push_back(Vector3(0,0,-cs*2));
-
- float ll = p3d->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_LOWER);
- float ul = p3d->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_UPPER);
- float lll = -p3d->get_param(SliderJoint::PARAM_LINEAR_LIMIT_LOWER);
- float lul = -p3d->get_param(SliderJoint::PARAM_LINEAR_LIMIT_UPPER);
-
- if (lll>lul) {
-
- cursor_points.push_back(Vector3(lul,0,0));
- cursor_points.push_back(Vector3(lll,0,0));
-
- cursor_points.push_back(Vector3(lul,-cs,-cs));
- cursor_points.push_back(Vector3(lul,-cs,cs));
- cursor_points.push_back(Vector3(lul,-cs,cs));
- cursor_points.push_back(Vector3(lul,cs,cs));
- cursor_points.push_back(Vector3(lul,cs,cs));
- cursor_points.push_back(Vector3(lul,cs,-cs));
- cursor_points.push_back(Vector3(lul,cs,-cs));
- cursor_points.push_back(Vector3(lul,-cs,-cs));
-
-
- cursor_points.push_back(Vector3(lll,-cs,-cs));
- cursor_points.push_back(Vector3(lll,-cs,cs));
- cursor_points.push_back(Vector3(lll,-cs,cs));
- cursor_points.push_back(Vector3(lll,cs,cs));
- cursor_points.push_back(Vector3(lll,cs,cs));
- cursor_points.push_back(Vector3(lll,cs,-cs));
- cursor_points.push_back(Vector3(lll,cs,-cs));
- cursor_points.push_back(Vector3(lll,-cs,-cs));
-
-
- } else {
-
- cursor_points.push_back(Vector3(+cs*2,0,0));
- cursor_points.push_back(Vector3(-cs*2,0,0));
-
- }
-
- if (ll<ul) {
-
- const int points = 32;
- float step = (ul-ll)/points;
-
-
- for(int i=0;i<points;i++) {
-
- float s = ll+i*(ul-ll)/points;
- float n = ll+(i+1)*(ul-ll)/points;
-
- Vector3 from=Vector3(0, Math::cos(s), -Math::sin(s) )*cs;
- Vector3 to=Vector3(0,Math::cos(n), -Math::sin(n) )*cs;
-
- if (i==points-1) {
- cursor_points.push_back(to);
- cursor_points.push_back(Vector3());
- }
- if (i==0) {
- cursor_points.push_back(from);
- cursor_points.push_back(Vector3());
- }
-
-
- cursor_points.push_back(from);
- cursor_points.push_back(to);
-
-
- }
-
- cursor_points.push_back(Vector3(0,cs*1.5,0));
- cursor_points.push_back(Vector3());
-
- } else {
-
-
- const int points = 32;
-
- for(int i=0;i<points;i++) {
-
- float s = ll+i*(Math_PI*2.0)/points;
- float n = ll+(i+1)*(Math_PI*2.0)/points;
-
- Vector3 from=Vector3(0,Math::cos(s),-Math::sin(s) )*cs;
- Vector3 to=Vector3( 0,Math::cos(n),-Math::sin(n) )*cs;
-
- cursor_points.push_back(from);
- cursor_points.push_back(to);
-
- }
-
- }
- add_collision_segments(cursor_points);
- add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
-
-}
-
-
-SliderJointSpatialGizmo::SliderJointSpatialGizmo(SliderJoint* p_p3d) {
-
- p3d=p_p3d;
- set_spatial_node(p3d);
-}
-
-///////
-///
-////
-
-void ConeTwistJointSpatialGizmo::redraw() {
-
- clear();
- float cs = 0.25;
- Vector<Vector3> points;
-
- float r = 1.0;
- float w = r*Math::sin(p3d->get_param(ConeTwistJoint::PARAM_SWING_SPAN));
- float d = r*Math::cos(p3d->get_param(ConeTwistJoint::PARAM_SWING_SPAN));
-
-
- //swing
- for(int i=0;i<360;i+=10) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+10);
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w;
-
- /*points.push_back(Vector3(a.x,0,a.y));
- points.push_back(Vector3(b.x,0,b.y));
- points.push_back(Vector3(0,a.x,a.y));
- points.push_back(Vector3(0,b.x,b.y));*/
- points.push_back(Vector3(d,a.x,a.y));
- points.push_back(Vector3(d,b.x,b.y));
-
- if (i%90==0) {
-
- points.push_back(Vector3(d,a.x,a.y));
- points.push_back(Vector3());
-
- }
- }
-
- points.push_back(Vector3());
- points.push_back(Vector3(1,0,0));
-
- //twist
- /*
- */
- float ts=Math::rad2deg(p3d->get_param(ConeTwistJoint::PARAM_TWIST_SPAN));
- ts=MIN(ts,720);
-
-
- for(int i=0;i<int(ts);i+=5) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+5);
- float c = i/720.0;
- float cn = (i+5)/720.0;
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w*c;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w*cn;
-
- /*points.push_back(Vector3(a.x,0,a.y));
- points.push_back(Vector3(b.x,0,b.y));
- points.push_back(Vector3(0,a.x,a.y));
- points.push_back(Vector3(0,b.x,b.y));*/
-
- points.push_back(Vector3(c,a.x,a.y));
- points.push_back(Vector3(cn,b.x,b.y));
-
- }
-
-
- add_collision_segments(points);
- add_lines(points,SpatialEditorGizmos::singleton->joint_material);
-
-}
-
-
-ConeTwistJointSpatialGizmo::ConeTwistJointSpatialGizmo(ConeTwistJoint* p_p3d) {
-
- p3d=p_p3d;
- set_spatial_node(p3d);
-}
-
-////////
-/// \brief SpatialEditorGizmos::singleton
-///
-///////
-///
-////
-
-void Generic6DOFJointSpatialGizmo::redraw() {
-
- clear();
- Vector<Vector3> cursor_points;
- float cs = 0.25;
-
- for(int ax=0;ax<3;ax++) {
- /*cursor_points.push_back(Vector3(+cs,0,0));
- cursor_points.push_back(Vector3(-cs,0,0));
- cursor_points.push_back(Vector3(0,+cs,0));
- cursor_points.push_back(Vector3(0,-cs,0));
- cursor_points.push_back(Vector3(0,0,+cs*2));
- cursor_points.push_back(Vector3(0,0,-cs*2)); */
-
- float ll;
- float ul;
- float lll;
- float lul;
-
- int a1,a2,a3;
- bool enable_ang;
- bool enable_lin;
-
- switch(ax) {
- case 0:
- ll = p3d->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT);
- ul = p3d->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT);
- lll = -p3d->get_param_x(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT);
- lul = -p3d->get_param_x(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT);
- enable_ang = p3d->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT);
- enable_lin = p3d->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT);
- a1=0;
- a2=1;
- a3=2;
- break;
- case 1:
- ll = p3d->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT);
- ul = p3d->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT);
- lll = -p3d->get_param_y(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT);
- lul = -p3d->get_param_y(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT);
- enable_ang = p3d->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT);
- enable_lin = p3d->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT);
- a1=2;
- a2=0;
- a3=1;
- break;
- case 2:
- ll = p3d->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT);
- ul = p3d->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT);
- lll = -p3d->get_param_z(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT);
- lul = -p3d->get_param_z(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT);
- enable_ang = p3d->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT);
- enable_lin = p3d->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT);
-
- a1=1;
- a2=2;
- a3=0;
- break;
- }
-
-#define ADD_VTX(x,y,z)\
- {\
- Vector3 v;\
- v[a1]=(x);\
- v[a2]=(y);\
- v[a3]=(z);\
- cursor_points.push_back(v);\
- }
-
-#define SET_VTX(what,x,y,z)\
- {\
- Vector3 v;\
- v[a1]=(x);\
- v[a2]=(y);\
- v[a3]=(z);\
- what=v;\
- }
-
-
-
-
- if (enable_lin && lll>=lul) {
-
- ADD_VTX(lul,0,0);
- ADD_VTX(lll,0,0);
-
- ADD_VTX(lul,-cs,-cs);
- ADD_VTX(lul,-cs,cs);
- ADD_VTX(lul,-cs,cs);
- ADD_VTX(lul,cs,cs);
- ADD_VTX(lul,cs,cs);
- ADD_VTX(lul,cs,-cs);
- ADD_VTX(lul,cs,-cs);
- ADD_VTX(lul,-cs,-cs);
-
-
- ADD_VTX(lll,-cs,-cs);
- ADD_VTX(lll,-cs,cs);
- ADD_VTX(lll,-cs,cs);
- ADD_VTX(lll,cs,cs);
- ADD_VTX(lll,cs,cs);
- ADD_VTX(lll,cs,-cs);
- ADD_VTX(lll,cs,-cs);
- ADD_VTX(lll,-cs,-cs);
-
-
- } else {
-
- ADD_VTX(+cs*2,0,0);
- ADD_VTX(-cs*2,0,0);
-
- }
-
- if (enable_ang && ll<=ul) {
-
- const int points = 32;
- float step = (ul-ll)/points;
-
-
- for(int i=0;i<points;i++) {
-
- float s = ll+i*(ul-ll)/points;
- float n = ll+(i+1)*(ul-ll)/points;
-
- Vector3 from;
- SET_VTX(from,0, Math::cos(s), -Math::sin(s) );
- from*=cs;
- Vector3 to;
- SET_VTX(to,0,Math::cos(n), -Math::sin(n));
- to*=cs;
-
- if (i==points-1) {
- cursor_points.push_back(to);
- cursor_points.push_back(Vector3());
- }
- if (i==0) {
- cursor_points.push_back(from);
- cursor_points.push_back(Vector3());
- }
-
-
- cursor_points.push_back(from);
- cursor_points.push_back(to);
-
-
- }
-
- ADD_VTX(0,cs*1.5,0);
- cursor_points.push_back(Vector3());
-
- } else {
-
-
- const int points = 32;
-
- for(int i=0;i<points;i++) {
-
- float s = ll+i*(Math_PI*2.0)/points;
- float n = ll+(i+1)*(Math_PI*2.0)/points;
-
-// Vector3 from=Vector3(0,Math::cos(s),-Math::sin(s) )*cs;
-// Vector3 to=Vector3( 0,Math::cos(n),-Math::sin(n) )*cs;
-
- Vector3 from;
- SET_VTX(from,0, Math::cos(s), -Math::sin(s) );
- from*=cs;
- Vector3 to;
- SET_VTX(to,0,Math::cos(n), -Math::sin(n));
- to*=cs;
-
- cursor_points.push_back(from);
- cursor_points.push_back(to);
-
- }
-
- }
- }
-
-#undef ADD_VTX
-#undef SET_VTX
-
- add_collision_segments(cursor_points);
- add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
-
-}
-
-
-Generic6DOFJointSpatialGizmo::Generic6DOFJointSpatialGizmo(Generic6DOFJoint* p_p3d) {
-
- p3d=p_p3d;
- set_spatial_node(p3d);
-}
-
-///////
-///
-////
-
-
-SpatialEditorGizmos *SpatialEditorGizmos::singleton=NULL;
-
-Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
-
- if (p_spatial->cast_to<Light>()) {
-
- Ref<LightSpatialGizmo> lsg = memnew( LightSpatialGizmo(p_spatial->cast_to<Light>()) );
- return lsg;
- }
-
- if (p_spatial->cast_to<Camera>()) {
-
- Ref<CameraSpatialGizmo> lsg = memnew( CameraSpatialGizmo(p_spatial->cast_to<Camera>()) );
- return lsg;
- }
-
- if (p_spatial->cast_to<Skeleton>()) {
-
- Ref<SkeletonSpatialGizmo> lsg = memnew( SkeletonSpatialGizmo(p_spatial->cast_to<Skeleton>()) );
- return lsg;
- }
-
-
- if (p_spatial->cast_to<Position3D>()) {
-
- Ref<Position3DSpatialGizmo> lsg = memnew( Position3DSpatialGizmo(p_spatial->cast_to<Position3D>()) );
- return lsg;
- }
-
- if (p_spatial->cast_to<MeshInstance>()) {
-
- Ref<MeshInstanceSpatialGizmo> misg = memnew( MeshInstanceSpatialGizmo(p_spatial->cast_to<MeshInstance>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<Room>()) {
-
- Ref<RoomSpatialGizmo> misg = memnew( RoomSpatialGizmo(p_spatial->cast_to<Room>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<NavigationMeshInstance>()) {
-
- Ref<NavigationMeshSpatialGizmo> misg = memnew( NavigationMeshSpatialGizmo(p_spatial->cast_to<NavigationMeshInstance>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<RayCast>()) {
-
- Ref<RayCastSpatialGizmo> misg = memnew( RayCastSpatialGizmo(p_spatial->cast_to<RayCast>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<Portal>()) {
-
- Ref<PortalSpatialGizmo> misg = memnew( PortalSpatialGizmo(p_spatial->cast_to<Portal>()) );
- return misg;
- }
-
-
- if (p_spatial->cast_to<TestCube>()) {
-
- Ref<TestCubeSpatialGizmo> misg = memnew( TestCubeSpatialGizmo(p_spatial->cast_to<TestCube>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<SpatialPlayer>()) {
-
- Ref<SpatialPlayerSpatialGizmo> misg = memnew( SpatialPlayerSpatialGizmo(p_spatial->cast_to<SpatialPlayer>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<CollisionShape>()) {
-
- Ref<CollisionShapeSpatialGizmo> misg = memnew( CollisionShapeSpatialGizmo(p_spatial->cast_to<CollisionShape>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<VisibilityNotifier>()) {
-
- Ref<VisibilityNotifierGizmo> misg = memnew( VisibilityNotifierGizmo(p_spatial->cast_to<VisibilityNotifier>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<VehicleWheel>()) {
-
- Ref<VehicleWheelSpatialGizmo> misg = memnew( VehicleWheelSpatialGizmo(p_spatial->cast_to<VehicleWheel>()) );
- return misg;
- }
- if (p_spatial->cast_to<PinJoint>()) {
-
- Ref<PinJointSpatialGizmo> misg = memnew( PinJointSpatialGizmo(p_spatial->cast_to<PinJoint>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<HingeJoint>()) {
-
- Ref<HingeJointSpatialGizmo> misg = memnew( HingeJointSpatialGizmo(p_spatial->cast_to<HingeJoint>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<SliderJoint>()) {
-
- Ref<SliderJointSpatialGizmo> misg = memnew( SliderJointSpatialGizmo(p_spatial->cast_to<SliderJoint>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<ConeTwistJoint>()) {
-
- Ref<ConeTwistJointSpatialGizmo> misg = memnew( ConeTwistJointSpatialGizmo(p_spatial->cast_to<ConeTwistJoint>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<Generic6DOFJoint>()) {
-
- Ref<Generic6DOFJointSpatialGizmo> misg = memnew( Generic6DOFJointSpatialGizmo(p_spatial->cast_to<Generic6DOFJoint>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<CollisionPolygon>()) {
-
- Ref<CollisionPolygonSpatialGizmo> misg = memnew( CollisionPolygonSpatialGizmo(p_spatial->cast_to<CollisionPolygon>()) );
- return misg;
- }
-
-
- return Ref<SpatialEditorGizmo>();
-}
-
-
-Ref<FixedMaterial> SpatialEditorGizmos::create_line_material(const Color& p_base_color) {
-
- Ref<FixedMaterial> line_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
- line_material->set_flag(Material::FLAG_UNSHADED, true);
- line_material->set_line_width(3.0);
- line_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- line_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, true);
- line_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,p_base_color);
-
- return line_material;
-
-}
-
-Ref<FixedMaterial> SpatialEditorGizmos::create_solid_material(const Color& p_base_color) {
-
- Ref<FixedMaterial> line_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
- line_material->set_flag(Material::FLAG_UNSHADED, true);
- line_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- line_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,p_base_color);
-
- return line_material;
-
-}
-
-SpatialEditorGizmos::SpatialEditorGizmos() {
-
- singleton=this;
-
- handle_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
- handle_material->set_flag(Material::FLAG_UNSHADED, true);
- handle_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(0.8,0.8,0.8));
-
- handle2_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
- handle2_material->set_flag(Material::FLAG_UNSHADED, true);
- handle2_material->set_fixed_flag(FixedMaterial::FLAG_USE_POINT_SIZE, true);
- handle_t = SpatialEditor::get_singleton()->get_icon("Editor3DHandle","EditorIcons");
- handle2_material->set_point_size(handle_t->get_width());
- handle2_material->set_texture(FixedMaterial::PARAM_DIFFUSE,handle_t);
- handle2_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1));
- handle2_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- handle2_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, true);
-
- light_material = create_line_material(Color(1,1,0.2));
-
- light_material_omni_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
- light_material_omni_icon->set_flag(Material::FLAG_UNSHADED, true);
- light_material_omni_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
- light_material_omni_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
- light_material_omni_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- light_material_omni_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
- light_material_omni_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoLight","EditorIcons"));
-
-
- light_material_directional_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
- light_material_directional_icon->set_flag(Material::FLAG_UNSHADED, true);
- light_material_directional_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
- light_material_directional_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
- light_material_directional_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- light_material_directional_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
- light_material_directional_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoDirectionalLight","EditorIcons"));
-
- camera_material = create_line_material(Color(1.0,0.5,1.0));
-
-
- navmesh_edge_material = create_line_material(Color(0.1,0.8,1.0));
- navmesh_solid_material = create_solid_material(Color(0.1,0.8,1.0,0.4));
- navmesh_edge_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, false);
- navmesh_solid_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
-
- navmesh_edge_material_disabled = create_line_material(Color(1.0,0.8,0.1));
- navmesh_solid_material_disabled = create_solid_material(Color(1.0,0.8,0.1,0.4));
- navmesh_edge_material_disabled->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, false);
- navmesh_solid_material_disabled->set_flag(Material::FLAG_DOUBLE_SIDED,true);
-
- skeleton_material = create_line_material(Color(0.6,1.0,0.3));
- skeleton_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
- skeleton_material->set_flag(Material::FLAG_UNSHADED,true);
- skeleton_material->set_flag(Material::FLAG_ONTOP,true);
- skeleton_material->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
-
- //position 3D Shared mesh
-
- pos3d_mesh = Ref<Mesh>( memnew( Mesh ) );
- {
-
- DVector<Vector3> cursor_points;
- DVector<Color> cursor_colors;
- float cs = 0.25;
- cursor_points.push_back(Vector3(+cs,0,0));
- cursor_points.push_back(Vector3(-cs,0,0));
- cursor_points.push_back(Vector3(0,+cs,0));
- cursor_points.push_back(Vector3(0,-cs,0));
- cursor_points.push_back(Vector3(0,0,+cs));
- cursor_points.push_back(Vector3(0,0,-cs));
- cursor_colors.push_back(Color(1,0.5,0.5,0.7));
- cursor_colors.push_back(Color(1,0.5,0.5,0.7));
- cursor_colors.push_back(Color(0.5,1,0.5,0.7));
- cursor_colors.push_back(Color(0.5,1,0.5,0.7));
- cursor_colors.push_back(Color(0.5,0.5,1,0.7));
- cursor_colors.push_back(Color(0.5,0.5,1,0.7));
-
- Ref<FixedMaterial> mat = memnew( FixedMaterial );
- mat->set_flag(Material::FLAG_UNSHADED,true);
- mat->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true);
- mat->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true);
- mat->set_line_width(3);
- Array d;
- d.resize(VS::ARRAY_MAX);
- d[Mesh::ARRAY_VERTEX]=cursor_points;
- d[Mesh::ARRAY_COLOR]=cursor_colors;
- pos3d_mesh->add_surface(Mesh::PRIMITIVE_LINES,d);
- pos3d_mesh->surface_set_material(0,mat);
- }
-
-
- sample_player_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
- sample_player_icon->set_flag(Material::FLAG_UNSHADED, true);
- sample_player_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
- sample_player_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
- sample_player_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- sample_player_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
- sample_player_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoSpatialSamplePlayer","EditorIcons"));
-
- room_material = create_line_material(Color(1.0,0.6,0.9));
- portal_material = create_line_material(Color(1.0,0.8,0.6));
- raycast_material = create_line_material(Color(1.0,0.8,0.6));
- car_wheel_material = create_line_material(Color(0.6,0.8,1.0));
- visibility_notifier_material = create_line_material(Color(1.0,0.5,1.0));
- joint_material = create_line_material(Color(0.6,0.8,1.0));
-
- stream_player_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
- stream_player_icon->set_flag(Material::FLAG_UNSHADED, true);
- stream_player_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
- stream_player_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
- stream_player_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- stream_player_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
- stream_player_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoSpatialStreamPlayer","EditorIcons"));
-
- visibility_notifier_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
- visibility_notifier_icon->set_flag(Material::FLAG_UNSHADED, true);
- visibility_notifier_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
- visibility_notifier_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
- visibility_notifier_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- visibility_notifier_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
- visibility_notifier_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("Visible","EditorIcons"));
-
- {
-
- DVector<Vector3> vertices;
-
-#undef ADD_VTX
-#define ADD_VTX(m_idx);\
- vertices.push_back( face_points[m_idx] );
-
- for (int i=0;i<6;i++) {
-
-
- Vector3 face_points[4];
-
- for (int j=0;j<4;j++) {
-
- float v[3];
- v[0]=1.0;
- v[1]=1-2*((j>>1)&1);
- v[2]=v[1]*(1-2*(j&1));
-
- for (int k=0;k<3;k++) {
-
- if (i<3)
- face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
- else
- face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
- }
- }
- //tri 1
- ADD_VTX(0);
- ADD_VTX(1);
- ADD_VTX(2);
- //tri 2
- ADD_VTX(2);
- ADD_VTX(3);
- ADD_VTX(0);
-
- }
-
- test_cube_tm = Ref<TriangleMesh>( memnew( TriangleMesh ) );
- test_cube_tm->create(vertices);
- }
-
- shape_material = create_line_material(Color(0.2,1,1.0));
-
-
-}
-
+/*************************************************************************/
+/* spatial_editor_gizmos.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "spatial_editor_gizmos.h"
+#include "geometry.h"
+#include "scene/3d/camera.h"
+#include "scene/resources/surface_tool.h"
+#include "scene/resources/sphere_shape.h"
+#include "scene/resources/box_shape.h"
+#include "scene/resources/capsule_shape.h"
+#include "scene/resources/ray_shape.h"
+#include "scene/resources/convex_polygon_shape.h"
+#include "scene/resources/plane_shape.h"
+#include "quick_hull.h"
+
+// Keep small children away from this file.
+// It's so ugly it will eat them alive
+
+#define HANDLE_HALF_SIZE 0.05
+
+void SpatialGizmoTool::clear() {
+
+ for(int i=0;i<instances.size();i++) {
+
+ if (instances[i].instance.is_valid())
+ VS::get_singleton()->free(instances[i].instance);
+
+
+ }
+
+ billboard_handle=false;
+ collision_segments.clear();
+ collision_mesh=Ref<TriangleMesh>();
+ instances.clear();
+ handles.clear();
+ secondary_handles.clear();
+}
+
+void SpatialGizmoTool::Instance::create_instance(Spatial *p_base) {
+
+ instance = VS::get_singleton()->instance_create2(mesh->get_rid(),p_base->get_world()->get_scenario());
+ VS::get_singleton()->instance_attach_object_instance_ID(instance,p_base->get_instance_ID());
+ if (billboard)
+ VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_BILLBOARD,true);
+ if (unscaled)
+ VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_DEPH_SCALE,true);
+ if (skeleton.is_valid())
+ VS::get_singleton()->instance_attach_skeleton(instance,skeleton);
+ if (extra_margin)
+ VS::get_singleton()->instance_set_extra_visibility_margin(instance,1);
+ VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_CAST_SHADOW,false);
+ VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_RECEIVE_SHADOWS,false);
+ VS::get_singleton()->instance_set_layer_mask(instance,1<<SpatialEditorViewport::GIZMO_EDIT_LAYER); //gizmos are 26
+}
+
+
+
+void SpatialGizmoTool::add_mesh(const Ref<Mesh>& p_mesh,bool p_billboard, const RID &p_skeleton) {
+
+ ERR_FAIL_COND(!spatial_node);
+ Instance ins;
+
+ ins.billboard=p_billboard;
+ ins.mesh=p_mesh;
+ ins.skeleton=p_skeleton;
+ if (valid) {
+ ins.create_instance(spatial_node);
+ VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform());
+ }
+
+ instances.push_back(ins);
+
+}
+
+void SpatialGizmoTool::add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material,bool p_billboard){
+
+ ERR_FAIL_COND(!spatial_node);
+ Instance ins;
+
+ Ref<Mesh> mesh = memnew( Mesh );
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+
+ a[Mesh::ARRAY_VERTEX]=p_lines;
+
+ DVector<Color> color;
+ color.resize(p_lines.size());
+ {
+ DVector<Color>::Write w = color.write();
+ for(int i=0;i<p_lines.size();i++) {
+ if (is_selected())
+ w[i]=Color(1,1,1,0.6);
+ else
+ w[i]=Color(1,1,1,0.25);
+ }
+
+ }
+
+ a[Mesh::ARRAY_COLOR]=color;
+
+
+ mesh->add_surface(Mesh::PRIMITIVE_LINES,a);
+ mesh->surface_set_material(0,p_material);
+
+ if (p_billboard) {
+ float md=0;
+ for(int i=0;i<p_lines.size();i++) {
+
+ md=MAX(0,p_lines[i].length());
+
+ }
+ if (md) {
+ mesh->set_custom_aabb(AABB(Vector3(-md,-md,-md),Vector3(md,md,md)*2.0));
+ }
+ }
+
+ ins.billboard=p_billboard;
+ ins.mesh=mesh;
+ if (valid) {
+ ins.create_instance(spatial_node);
+ VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform());
+ }
+
+ instances.push_back(ins);
+
+}
+
+void SpatialGizmoTool::add_unscaled_billboard(const Ref<Material>& p_material,float p_scale) {
+
+ ERR_FAIL_COND(!spatial_node);
+ Instance ins;
+
+ Vector<Vector3 > vs;
+ Vector<Vector2 > uv;
+
+ vs.push_back(Vector3(-p_scale,p_scale,0));
+ vs.push_back(Vector3(p_scale,p_scale,0));
+ vs.push_back(Vector3(p_scale,-p_scale,0));
+ vs.push_back(Vector3(-p_scale,-p_scale,0));
+
+ uv.push_back(Vector2(1,0));
+ uv.push_back(Vector2(0,0));
+ uv.push_back(Vector2(0,1));
+ uv.push_back(Vector2(1,1));
+
+ Ref<Mesh> mesh = memnew( Mesh );
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+ a[Mesh::ARRAY_VERTEX]=vs;
+ a[Mesh::ARRAY_TEX_UV]=uv;
+ mesh->add_surface(Mesh::PRIMITIVE_TRIANGLE_FAN,a);
+ mesh->surface_set_material(0,p_material);
+
+ if (true) {
+ float md=0;
+ for(int i=0;i<vs.size();i++) {
+
+ md=MAX(0,vs[i].length());
+
+ }
+ if (md) {
+ mesh->set_custom_aabb(AABB(Vector3(-md,-md,-md),Vector3(md,md,md)*2.0));
+ }
+ }
+
+ ins.mesh=mesh;
+ ins.unscaled=true;
+ ins.billboard=true;
+ if (valid) {
+ ins.create_instance(spatial_node);
+ VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform());
+ }
+
+ instances.push_back(ins);
+
+
+}
+
+void SpatialGizmoTool::add_collision_triangles(const Ref<TriangleMesh>& p_tmesh) {
+
+ collision_mesh=p_tmesh;
+}
+
+void SpatialGizmoTool::add_collision_segments(const Vector<Vector3> &p_lines) {
+
+ int from=collision_segments.size();
+ collision_segments.resize(from+p_lines.size());
+ for(int i=0;i<p_lines.size();i++) {
+
+ collision_segments[from+i]=p_lines[i];
+ }
+}
+
+
+void SpatialGizmoTool::add_handles(const Vector<Vector3> &p_handles, bool p_billboard,bool p_secondary){
+
+ billboard_handle=p_billboard;
+
+ if (!is_selected())
+ return;
+
+ ERR_FAIL_COND(!spatial_node);
+
+ ERR_FAIL_COND(!spatial_node);
+ Instance ins;
+
+
+ Ref<Mesh> mesh = memnew( Mesh );
+#if 1
+
+ Array a;
+ a.resize(VS::ARRAY_MAX);
+ a[VS::ARRAY_VERTEX]=p_handles;
+ DVector<Color> colors;
+ {
+ colors.resize(p_handles.size());
+ DVector<Color>::Write w=colors.write();
+ for(int i=0;i<p_handles.size();i++) {
+
+ Color col(1,1,1,1);
+ if (SpatialEditor::get_singleton()->get_over_gizmo_handle()!=i)
+ col=Color(0.9,0.9,0.9,0.9);
+ w[i]=col;
+ }
+
+ }
+ a[VS::ARRAY_COLOR]=colors;
+ mesh->add_surface(Mesh::PRIMITIVE_POINTS,a);
+ mesh->surface_set_material(0,SpatialEditorGizmos::singleton->handle2_material);
+
+ if (p_billboard) {
+ float md=0;
+ for(int i=0;i<p_handles.size();i++) {
+
+ md=MAX(0,p_handles[i].length());
+
+ }
+ if (md) {
+ mesh->set_custom_aabb(AABB(Vector3(-md,-md,-md),Vector3(md,md,md)*2.0));
+ }
+ }
+
+
+
+#else
+ for(int ih=0;ih<p_handles.size();ih++) {
+
+
+ Vector<Vector3> vertices;
+ Vector<Vector3> normals;
+
+ int vtx_idx=0;
+#define ADD_VTX(m_idx);\
+ vertices.push_back( (face_points[m_idx]*HANDLE_HALF_SIZE+p_handles[ih]) );\
+ normals.push_back( normal_points[m_idx] );\
+ vtx_idx++;\
+
+ for (int i=0;i<6;i++) {
+
+
+ Vector3 face_points[4];
+ Vector3 normal_points[4];
+ float uv_points[8]={0,0,0,1,1,1,1,0};
+
+ for (int j=0;j<4;j++) {
+
+ float v[3];
+ v[0]=1.0;
+ v[1]=1-2*((j>>1)&1);
+ v[2]=v[1]*(1-2*(j&1));
+
+ for (int k=0;k<3;k++) {
+
+ if (i<3)
+ face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ else
+ face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ }
+ normal_points[j]=Vector3();
+ normal_points[j][i%3]=(i>=3?-1:1);
+ }
+ //tri 1
+ ADD_VTX(0);
+ ADD_VTX(1);
+ ADD_VTX(2);
+ //tri 2
+ ADD_VTX(2);
+ ADD_VTX(3);
+ ADD_VTX(0);
+
+ }
+
+
+ Array d;
+ d.resize(VS::ARRAY_MAX);
+ d[VisualServer::ARRAY_NORMAL]= normals ;
+ d[VisualServer::ARRAY_VERTEX]= vertices ;
+
+ mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,d);
+ mesh->surface_set_material(ih,SpatialEditorGizmos::singleton->handle_material);
+
+
+ }
+#endif
+ ins.mesh=mesh;
+ ins.billboard=p_billboard;
+ ins.extra_margin=true;
+ if (valid) {
+ ins.create_instance(spatial_node);
+ VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform());
+ }
+ instances.push_back(ins);
+ if (!p_secondary) {
+ int chs=handles.size();
+ handles.resize(chs+p_handles.size());
+ for(int i=0;i<p_handles.size();i++) {
+ handles[i+chs]=p_handles[i];
+ }
+ } else {
+
+ int chs=secondary_handles.size();
+ secondary_handles.resize(chs+p_handles.size());
+ for(int i=0;i<p_handles.size();i++) {
+ secondary_handles[i+chs]=p_handles[i];
+ }
+
+ }
+
+}
+
+
+void SpatialGizmoTool::set_spatial_node(Spatial *p_node){
+
+ spatial_node=p_node;
+
+}
+
+bool SpatialGizmoTool::intersect_frustum(const Camera *p_camera,const Vector<Plane> &p_frustum) {
+
+ ERR_FAIL_COND_V(!spatial_node,false);
+ ERR_FAIL_COND_V(!valid,false);
+
+ if (collision_segments.size()) {
+
+ const Plane *p=p_frustum.ptr();
+ int fc=p_frustum.size();
+
+ int vc=collision_segments.size();
+ const Vector3* vptr=collision_segments.ptr();
+ Transform t = spatial_node->get_global_transform();
+
+ for(int i=0;i<vc/2;i++) {
+
+
+ Vector3 a=t.xform(vptr[i*2+0]);
+ Vector3 b=t.xform(vptr[i*2+1]);
+
+ bool any_out=false;
+ for(int j=0;j<fc;j++) {
+
+ if (p[j].distance_to(a) > 0 && p[j].distance_to(b) >0) {
+
+ any_out=true;
+ break;
+ }
+ }
+
+ if (!any_out)
+ return true;
+ }
+
+ return false;
+ }
+
+ return false;
+}
+
+
+bool SpatialGizmoTool::intersect_ray(const Camera *p_camera,const Point2& p_point, Vector3& r_pos, Vector3& r_normal,int *r_gizmo_handle,bool p_sec_first) {
+
+ ERR_FAIL_COND_V(!spatial_node,false);
+ ERR_FAIL_COND_V(!valid,false);
+
+ if (r_gizmo_handle) {
+
+ Transform t = spatial_node->get_global_transform();
+ t.orthonormalize();
+ if (billboard_handle) {
+ t.set_look_at(t.origin,t.origin+p_camera->get_transform().basis.get_axis(2),p_camera->get_transform().basis.get_axis(1));
+ }
+ Transform ti=t.affine_inverse();
+
+ Vector3 ray_from=ti.xform(p_camera->project_ray_origin(p_point));
+ Vector3 ray_dir=t.basis.xform_inv(p_camera->project_ray_normal(p_point)).normalized();
+ Vector3 ray_to = ray_from+ray_dir*4096;
+
+ float min_d=1e20;
+ int idx=-1;
+
+ for(int i=0;i<secondary_handles.size();i++) {
+#if 1
+
+
+ Vector3 hpos = t.xform(secondary_handles[i]);
+ Vector2 p = p_camera->unproject_position(hpos);
+ if (p.distance_to(p_point)<SpatialEditorGizmos::singleton->handle_t->get_width()*0.6) {
+
+
+ real_t dp = p_camera->get_transform().origin.distance_to(hpos);
+ if (dp<min_d) {
+
+ r_pos=t.xform(hpos);
+ r_normal=p_camera->get_transform().basis.get_axis(2);
+ min_d=dp;
+ idx=i+handles.size();
+
+ }
+
+ }
+
+#else
+ AABB aabb;
+ aabb.pos=Vector3(-1,-1,-1)*HANDLE_HALF_SIZE;
+ aabb.size=aabb.pos*-2;
+ aabb.pos+=secondary_handles[i];
+
+
+ Vector3 rpos,rnorm;
+
+ if (aabb.intersects_segment(ray_from,ray_to,&rpos,&rnorm)) {
+
+ real_t dp = ray_dir.dot(rpos);
+ if (dp<min_d) {
+
+ r_pos=t.xform(rpos);
+ r_normal=ti.basis.xform_inv(rnorm).normalized();
+ min_d=dp;
+ idx=i+handles.size();
+
+ }
+ }
+#endif
+ }
+
+ if (p_sec_first && idx!=-1) {
+
+ *r_gizmo_handle=idx;
+ return true;
+ }
+
+ min_d=1e20;
+
+ for(int i=0;i<handles.size();i++) {
+
+#if 1
+
+
+ Vector3 hpos = t.xform(handles[i]);
+ Vector2 p = p_camera->unproject_position(hpos);
+ if (p.distance_to(p_point)<SpatialEditorGizmos::singleton->handle_t->get_width()*0.6) {
+
+
+ real_t dp = p_camera->get_transform().origin.distance_to(hpos);
+ if (dp<min_d) {
+
+ r_pos=t.xform(hpos);
+ r_normal=p_camera->get_transform().basis.get_axis(2);
+ min_d=dp;
+ idx=i;
+
+ }
+
+ }
+
+#else
+
+ AABB aabb;
+ aabb.pos=Vector3(-1,-1,-1)*HANDLE_HALF_SIZE;
+ aabb.size=aabb.pos*-2;
+ aabb.pos+=handles[i];
+
+
+ Vector3 rpos,rnorm;
+
+ if (aabb.intersects_segment(ray_from,ray_to,&rpos,&rnorm)) {
+
+ real_t dp = ray_dir.dot(rpos);
+ if (dp<min_d) {
+
+ r_pos=t.xform(rpos);
+ r_normal=ti.basis.xform_inv(rnorm).normalized();
+ min_d=dp;
+ idx=i;
+
+ }
+ }
+#endif
+ }
+
+ if (idx>=0) {
+ *r_gizmo_handle=idx;
+ return true;
+ }
+
+
+ }
+
+ if (collision_segments.size()) {
+
+ Plane camp(p_camera->get_transform().origin,(-p_camera->get_transform().basis.get_axis(2)).normalized());
+
+ int vc=collision_segments.size();
+ const Vector3* vptr=collision_segments.ptr();
+ Transform t = spatial_node->get_global_transform();
+ if (billboard_handle) {
+ t.set_look_at(t.origin,t.origin+p_camera->get_transform().basis.get_axis(2),p_camera->get_transform().basis.get_axis(1));
+ }
+
+ Vector3 cp;
+ float cpd=1e20;
+
+ for(int i=0;i<vc/2;i++) {
+
+
+ Vector3 a=t.xform(vptr[i*2+0]);
+ Vector3 b=t.xform(vptr[i*2+1]);
+ Vector2 s[2];
+ s[0] = p_camera->unproject_position(a);
+ s[1] = p_camera->unproject_position(b);
+
+
+ Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point,s);
+
+ float pd = p.distance_to(p_point);
+
+ if (pd<cpd) {
+
+
+ float d = s[0].distance_to(s[1]);
+ Vector3 tcp;
+ if (d>0) {
+
+ float d2=s[0].distance_to(p)/d;
+ tcp = a+(b-a)*d2;
+
+ } else {
+ tcp=a;
+
+ }
+
+ if (camp.distance_to(tcp)<p_camera->get_znear())
+ continue;
+ cp=tcp;
+ cpd=pd;
+ }
+ }
+
+ if (cpd<8) {
+
+ r_pos=cp;
+ r_normal=-p_camera->project_ray_normal(p_point);
+ return true;
+ }
+
+ return false;
+ }
+
+
+ if (collision_mesh.is_valid()) {
+ Transform gt = spatial_node->get_global_transform();
+
+ if (billboard_handle) {
+ gt.set_look_at(gt.origin,gt.origin+p_camera->get_transform().basis.get_axis(2),p_camera->get_transform().basis.get_axis(1));
+ }
+
+ Transform ai=gt.affine_inverse();
+ Vector3 ray_from = ai.xform(p_camera->project_ray_origin(p_point));
+ Vector3 ray_dir=ai.basis.xform(p_camera->project_ray_normal(p_point)).normalized();
+ Vector3 rpos,rnorm;
+
+#if 1
+
+
+
+ if (collision_mesh->intersect_ray(ray_from,ray_dir,rpos,rnorm)) {
+
+ r_pos=gt.xform(rpos);
+ r_normal=gt.basis.xform(rnorm).normalized();
+ return true;
+ }
+#else
+
+ if (collision_mesh->intersect_segment(ray_from,ray_from+ray_dir*4906.0,rpos,rnorm)) {
+
+ r_pos=gt.xform(rpos);
+ r_normal=gt.basis.xform(rnorm).normalized();
+ return true;
+ }
+
+#endif
+ }
+
+ return false;
+
+}
+
+
+
+void SpatialGizmoTool::create() {
+
+ ERR_FAIL_COND(!spatial_node);
+ ERR_FAIL_COND(valid);
+ valid=true;
+
+ for(int i=0;i<instances.size();i++) {
+
+ instances[i].create_instance(spatial_node);
+ }
+
+ transform();
+
+}
+
+void SpatialGizmoTool::transform(){
+
+ ERR_FAIL_COND(!spatial_node);
+ ERR_FAIL_COND(!valid);
+ for(int i=0;i<instances.size();i++) {
+ VS::get_singleton()->instance_set_transform(instances[i].instance,spatial_node->get_global_transform());
+ }
+
+}
+
+
+void SpatialGizmoTool::free(){
+
+ ERR_FAIL_COND(!spatial_node);
+ ERR_FAIL_COND(!valid);
+
+ for(int i=0;i<instances.size();i++) {
+
+ if (instances[i].instance.is_valid())
+ VS::get_singleton()->free(instances[i].instance);
+ instances[i].instance=RID();
+ }
+
+ valid=false;
+
+
+}
+
+
+
+SpatialGizmoTool::SpatialGizmoTool() {
+ valid=false;
+ billboard_handle=false;
+
+}
+
+SpatialGizmoTool::~SpatialGizmoTool(){
+
+ clear();
+}
+
+Vector3 SpatialGizmoTool::get_handle_pos(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,handles.size(),Vector3());
+
+ return handles[p_idx];
+
+}
+
+//// light gizmo
+
+
+String LightSpatialGizmo::get_handle_name(int p_idx) const {
+
+ if (p_idx==0)
+ return "Radius";
+ else
+ return "Aperture";
+}
+
+
+Variant LightSpatialGizmo::get_handle_value(int p_idx) const{
+
+ if (p_idx==0)
+ return light->get_parameter(Light::PARAM_RADIUS);
+ if (p_idx==1)
+ return light->get_parameter(Light::PARAM_SPOT_ANGLE);
+
+ return Variant();
+}
+
+
+static float _find_closest_angle_to_half_pi_arc(const Vector3& p_from, const Vector3& p_to, float p_arc_radius,const Transform& p_arc_xform) {
+
+ //bleh, discrete is simpler
+ static const int arc_test_points=64;
+ float min_d = 1e20;
+ Vector3 min_p;
+
+
+ for(int i=0;i<arc_test_points;i++) {
+
+ float a = i*Math_PI*0.5/arc_test_points;
+ float an = (i+1)*Math_PI*0.5/arc_test_points;
+ Vector3 p=Vector3( Math::cos(a), 0, -Math::sin(a) )*p_arc_radius;
+ Vector3 n=Vector3( Math::cos(an), 0,- Math::sin(an) )*p_arc_radius;
+
+ Vector3 ra,rb;
+ Geometry::get_closest_points_between_segments(p,n,p_from,p_to,ra,rb);
+
+ float d = ra.distance_to(rb);
+ if (d<min_d) {
+ min_d=d;
+ min_p=ra;
+ }
+
+ }
+
+ //min_p = p_arc_xform.affine_inverse().xform(min_p);
+ float a = Vector2(min_p.x,-min_p.z).angle();
+ return a*180.0/Math_PI;
+}
+
+
+void LightSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point) {
+
+ Transform gt = light->get_global_transform();
+ gt.orthonormalize();
+ Transform gi = gt.affine_inverse();
+
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ Vector3 s[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)};
+ if (p_idx==0) {
+
+ if (light->cast_to<SpotLight>()) {
+ Vector3 ra,rb;
+ Geometry::get_closest_points_between_segments(Vector3(),Vector3(0,0,-4096),s[0],s[1],ra,rb);
+
+ float d = -ra.z;
+ if (d<0)
+ d=0;
+
+ light->set_parameter(Light::PARAM_RADIUS,d);
+ } else if (light->cast_to<OmniLight>()) {
+
+ Plane cp=Plane( gt.origin, p_camera->get_transform().basis.get_axis(2));
+
+ Vector3 inters;
+ if (cp.intersects_ray(ray_from,ray_dir,&inters)) {
+
+ float r = inters.distance_to(gt.origin);
+ light->set_parameter(Light::PARAM_RADIUS,r);
+ }
+
+ }
+
+ } else if (p_idx==1) {
+
+ float a = _find_closest_angle_to_half_pi_arc(s[0],s[1],light->get_parameter(Light::PARAM_RADIUS),gt);
+ light->set_parameter(Light::PARAM_SPOT_ANGLE,CLAMP(a,0.01,89.99));
+ }
+}
+
+void LightSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
+
+ if (p_cancel) {
+
+ light->set_parameter(p_idx==0?Light::PARAM_RADIUS:Light::PARAM_SPOT_ANGLE,p_restore);
+
+ } else if (p_idx==0) {
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ ur->create_action("Change Light Radius");
+ ur->add_do_method(light,"set_parameter",Light::PARAM_RADIUS,light->get_parameter(Light::PARAM_RADIUS));
+ ur->add_undo_method(light,"set_parameter",Light::PARAM_RADIUS,p_restore);
+ ur->commit_action();
+ } else if (p_idx==1) {
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ ur->create_action("Change Light Radius");
+ ur->add_do_method(light,"set_parameter",Light::PARAM_SPOT_ANGLE,light->get_parameter(Light::PARAM_SPOT_ANGLE));
+ ur->add_undo_method(light,"set_parameter",Light::PARAM_SPOT_ANGLE,p_restore);
+ ur->commit_action();
+
+ }
+}
+
+
+
+void LightSpatialGizmo::redraw() {
+
+
+ if (light->cast_to<DirectionalLight>()) {
+
+
+
+ const int arrow_points=5;
+ Vector3 arrow[arrow_points]={
+ Vector3(0,0,2),
+ Vector3(1,1,2),
+ Vector3(1,1,-1),
+ Vector3(2,2,-1),
+ Vector3(0,0,-3)
+ };
+
+ int arrow_sides=4;
+
+ Vector<Vector3> lines;
+
+
+ for(int i = 0; i < arrow_sides ; i++) {
+
+
+ Matrix3 ma(Vector3(0,0,1),Math_PI*2*float(i)/arrow_sides);
+ Matrix3 mb(Vector3(0,0,1),Math_PI*2*float(i+1)/arrow_sides);
+
+
+ for(int j=1;j<arrow_points-1;j++) {
+
+ if (j!=2) {
+ lines.push_back(ma.xform(arrow[j]));
+ lines.push_back(ma.xform(arrow[j+1]));
+ }
+ if (j<arrow_points-1) {
+ lines.push_back(ma.xform(arrow[j]));
+ lines.push_back(mb.xform(arrow[j]));
+ }
+
+ }
+ }
+
+ add_lines(lines,SpatialEditorGizmos::singleton->light_material);
+ add_collision_segments(lines);
+ add_unscaled_billboard(SpatialEditorGizmos::singleton->light_material_directional_icon,0.05);
+
+ }
+
+ if (light->cast_to<OmniLight>()) {
+
+ clear();
+
+
+ OmniLight *on = light->cast_to<OmniLight>();
+
+ float r = on->get_parameter(Light::PARAM_RADIUS);
+
+ Vector<Vector3> points;
+
+ for(int i=0;i<=360;i++) {
+
+ float ra=Math::deg2rad(i);
+ float rb=Math::deg2rad(i+1);
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
+
+ /*points.push_back(Vector3(a.x,0,a.y));
+ points.push_back(Vector3(b.x,0,b.y));
+ points.push_back(Vector3(0,a.x,a.y));
+ points.push_back(Vector3(0,b.x,b.y));*/
+ points.push_back(Vector3(a.x,a.y,0));
+ points.push_back(Vector3(b.x,b.y,0));
+
+ }
+
+ add_lines(points,SpatialEditorGizmos::singleton->light_material,true);
+ add_collision_segments(points);
+
+ add_unscaled_billboard(SpatialEditorGizmos::singleton->light_material_omni_icon,0.05);
+
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(r,0,0));
+ add_handles(handles,true);
+
+
+ }
+
+
+ if (light->cast_to<SpotLight>()) {
+
+ clear();
+
+ Vector<Vector3> points;
+ SpotLight *on = light->cast_to<SpotLight>();
+
+ float r = on->get_parameter(Light::PARAM_RADIUS);
+ float w = r*Math::sin(Math::deg2rad(on->get_parameter(Light::PARAM_SPOT_ANGLE)));
+ float d = r*Math::cos(Math::deg2rad(on->get_parameter(Light::PARAM_SPOT_ANGLE)));
+
+
+
+ for(int i=0;i<360;i++) {
+
+ float ra=Math::deg2rad(i);
+ float rb=Math::deg2rad(i+1);
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w;
+
+ /*points.push_back(Vector3(a.x,0,a.y));
+ points.push_back(Vector3(b.x,0,b.y));
+ points.push_back(Vector3(0,a.x,a.y));
+ points.push_back(Vector3(0,b.x,b.y));*/
+ points.push_back(Vector3(a.x,a.y,-d));
+ points.push_back(Vector3(b.x,b.y,-d));
+
+ if (i%90==0) {
+
+ points.push_back(Vector3(a.x,a.y,-d));
+ points.push_back(Vector3());
+
+ }
+
+
+ }
+
+ points.push_back(Vector3(0,0,-r));
+ points.push_back(Vector3());
+
+ add_lines(points,SpatialEditorGizmos::singleton->light_material);
+
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(0,0,-r));
+
+ Vector<Vector3> collision_segments;
+
+ for(int i=0;i<64;i++) {
+
+ float ra=i*Math_PI*2.0/64.0;
+ float rb=(i+1)*Math_PI*2.0/64.0;
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w;
+
+ collision_segments.push_back(Vector3(a.x,a.y,-d));
+ collision_segments.push_back(Vector3(b.x,b.y,-d));
+
+ if (i%16==0) {
+
+ collision_segments.push_back(Vector3(a.x,a.y,-d));
+ collision_segments.push_back(Vector3());
+
+ }
+
+ if (i==16) {
+
+ handles.push_back(Vector3(a.x,a.y,-d));
+ }
+
+ }
+
+ collision_segments.push_back(Vector3(0,0,-r));
+ collision_segments.push_back(Vector3());
+
+
+ add_handles(handles);
+ add_collision_segments(collision_segments);
+ add_unscaled_billboard(SpatialEditorGizmos::singleton->light_material_omni_icon,0.05);
+
+ }
+
+}
+
+LightSpatialGizmo::LightSpatialGizmo(Light* p_light){
+
+ light=p_light;
+ set_spatial_node(p_light);
+
+}
+
+//////
+
+String CameraSpatialGizmo::get_handle_name(int p_idx) const {
+
+ if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) {
+ return "FOV";
+ } else {
+ return "Size";
+ }
+}
+Variant CameraSpatialGizmo::get_handle_value(int p_idx) const{
+
+ if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) {
+ return camera->get_fov();
+ } else {
+
+ return camera->get_size();
+ }
+}
+void CameraSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){
+
+ Transform gt = camera->get_global_transform();
+ gt.orthonormalize();
+ Transform gi = gt.affine_inverse();
+
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ Vector3 s[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)};
+
+ if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) {
+ Transform gt=camera->get_global_transform();
+ float a = _find_closest_angle_to_half_pi_arc(s[0],s[1],1.0,gt);
+ camera->set("fov",a);
+ } else {
+
+ Vector3 ra,rb;
+ Geometry::get_closest_points_between_segments(Vector3(0,0,-1),Vector3(4096,0,-1),s[0],s[1],ra,rb);
+ float d = ra.x * 2.0;
+ if (d<0)
+ d=0;
+
+ camera->set("size",d);
+ }
+
+}
+void CameraSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
+
+ if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) {
+
+ if (p_cancel) {
+
+ camera->set("fov",p_restore);
+ } else {
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ ur->create_action("Change Camera FOV");
+ ur->add_do_property(camera,"fov",camera->get_fov());
+ ur->add_undo_property(camera,"fov",p_restore);
+ ur->commit_action();
+ }
+
+ } else {
+
+ if (p_cancel) {
+
+ camera->set("size",p_restore);
+ } else {
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ ur->create_action("Change Camera Size");
+ ur->add_do_property(camera,"size",camera->get_size());
+ ur->add_undo_property(camera,"size",p_restore);
+ ur->commit_action();
+ }
+
+ }
+
+}
+
+void CameraSpatialGizmo::redraw(){
+
+ clear();
+
+ Vector<Vector3> lines;
+ Vector<Vector3> handles;
+
+
+ switch(camera->get_projection()) {
+
+ case Camera::PROJECTION_PERSPECTIVE: {
+
+ float fov = camera->get_fov();
+
+ Vector3 side=Vector3( Math::sin(Math::deg2rad(fov)), 0, -Math::cos(Math::deg2rad(fov)) );
+ Vector3 nside=side;
+ nside.x=-nside.x;
+ Vector3 up=Vector3(0,side.x,0);
+
+
+#define ADD_TRIANGLE( m_a, m_b, m_c)\
+{\
+ lines.push_back(m_a);\
+ lines.push_back(m_b);\
+ lines.push_back(m_b);\
+ lines.push_back(m_c);\
+ lines.push_back(m_c);\
+ lines.push_back(m_a);\
+}
+
+ ADD_TRIANGLE( Vector3(), side+up, side-up );
+ ADD_TRIANGLE( Vector3(), nside+up, nside-up );
+ ADD_TRIANGLE( Vector3(), side+up, nside+up );
+ ADD_TRIANGLE( Vector3(), side-up, nside-up );
+
+ handles.push_back(side);
+ side.x*=0.25;
+ nside.x*=0.25;
+ Vector3 tup( 0, up.y*3/2,side.z);
+ ADD_TRIANGLE( tup, side+up, nside+up );
+
+ } break;
+ case Camera::PROJECTION_ORTHOGONAL: {
+
+#define ADD_QUAD( m_a, m_b, m_c, m_d)\
+{\
+ lines.push_back(m_a);\
+ lines.push_back(m_b);\
+ lines.push_back(m_b);\
+ lines.push_back(m_c);\
+ lines.push_back(m_c);\
+ lines.push_back(m_d);\
+ lines.push_back(m_d);\
+ lines.push_back(m_a);\
+}
+ float size = camera->get_size();
+
+ float hsize=size*0.5;
+ Vector3 right(hsize,0,0);
+ Vector3 up(0,hsize,0);
+ Vector3 back(0,0,-1.0);
+ Vector3 front(0,0,0);
+
+ ADD_QUAD( -up-right,-up+right,up+right,up-right);
+ ADD_QUAD( -up-right+back,-up+right+back,up+right+back,up-right+back);
+ ADD_QUAD( up+right,up+right+back,up-right+back,up-right);
+ ADD_QUAD( -up+right,-up+right+back,-up-right+back,-up-right);
+ handles.push_back(right+back);
+
+ right.x*=0.25;
+ Vector3 tup( 0, up.y*3/2,back.z );
+ ADD_TRIANGLE( tup, right+up+back, -right+up+back );
+
+ } break;
+
+ }
+
+ add_lines(lines,SpatialEditorGizmos::singleton->camera_material);
+ add_collision_segments(lines);
+ add_handles(handles);
+}
+
+
+CameraSpatialGizmo::CameraSpatialGizmo(Camera* p_camera){
+
+ camera=p_camera;
+ set_spatial_node(camera);
+}
+
+
+
+
+//////
+
+void MeshInstanceSpatialGizmo::redraw() {
+
+ Ref<Mesh> m = mesh->get_mesh();
+ if (!m.is_valid())
+ return; //none
+
+ Ref<TriangleMesh> tm = m->generate_triangle_mesh();
+ if (tm.is_valid())
+ add_collision_triangles(tm);
+}
+
+MeshInstanceSpatialGizmo::MeshInstanceSpatialGizmo(MeshInstance* p_mesh) {
+
+ mesh=p_mesh;
+ set_spatial_node(p_mesh);
+}
+
+/////
+
+
+void Position3DSpatialGizmo::redraw() {
+
+ clear();
+ add_mesh(SpatialEditorGizmos::singleton->pos3d_mesh);
+ Vector<Vector3> cursor_points;
+ float cs = 0.25;
+ cursor_points.push_back(Vector3(+cs,0,0));
+ cursor_points.push_back(Vector3(-cs,0,0));
+ cursor_points.push_back(Vector3(0,+cs,0));
+ cursor_points.push_back(Vector3(0,-cs,0));
+ cursor_points.push_back(Vector3(0,0,+cs));
+ cursor_points.push_back(Vector3(0,0,-cs));
+ add_collision_segments(cursor_points);
+
+}
+
+
+Position3DSpatialGizmo::Position3DSpatialGizmo(Position3D* p_p3d) {
+
+ p3d=p_p3d;
+ set_spatial_node(p3d);
+}
+
+
+/////
+
+void SkeletonSpatialGizmo::redraw() {
+
+ clear();
+
+ Ref<SurfaceTool> surface_tool( memnew( SurfaceTool ));
+
+
+ surface_tool->begin(Mesh::PRIMITIVE_LINES);
+ surface_tool->set_material(SpatialEditorGizmos::singleton->skeleton_material);
+ Vector<Transform> grests;
+ grests.resize(skel->get_bone_count());
+
+ Vector<int> bones;
+ Vector<float> weights;
+ bones.resize(4);
+ weights.resize(4);
+
+ for(int i=0;i<4;i++) {
+ bones[i]=0;
+ weights[i]=0;
+ }
+
+ weights[0]=1;
+
+
+ AABB aabb;
+
+ Color bonecolor = Color(1.0,0.4,0.4,0.3);
+ Color rootcolor = Color(0.4,1.0,0.4,0.1);
+
+ for (int i=0;i<skel->get_bone_count();i++) {
+
+ int parent = skel->get_bone_parent(i);
+
+ if (parent>=0) {
+ grests[i]=grests[parent] * skel->get_bone_rest(i);
+
+ Vector3 v0 = grests[parent].origin;
+ Vector3 v1 = grests[i].origin;
+ Vector3 d = (v1-v0).normalized();
+ float dist = v0.distance_to(v1);
+
+ //find closest axis
+ int closest=-1;
+ float closest_d = 0.0;
+
+ for(int j=0;j<3;j++) {
+ float dp = Math::abs(grests[parent].basis[j].normalized().dot(d));
+ if (j==0 || dp>closest_d)
+ closest=j;
+ }
+
+ //find closest other
+ Vector3 first;
+ Vector3 points[4];
+ int pointidx=0;
+ for(int j=0;j<3;j++) {
+
+ bones[0]=parent;
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(rootcolor);
+ surface_tool->add_vertex(v0-grests[parent].basis[j].normalized()*dist*0.05);
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(rootcolor);
+ surface_tool->add_vertex(v0+grests[parent].basis[j].normalized()*dist*0.05);
+
+ if (j==closest)
+ continue;
+
+ Vector3 axis;
+ if (first==Vector3()) {
+ axis = d.cross(d.cross(grests[parent].basis[j])).normalized();
+ first=axis;
+ } else {
+ axis = d.cross(first).normalized();
+ }
+
+ for(int k=0;k<2;k++) {
+
+ if (k==1)
+ axis=-axis;
+ Vector3 point = v0+d*dist*0.2;
+ point+=axis*dist*0.1;
+
+
+ bones[0]=parent;
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(bonecolor);
+ surface_tool->add_vertex(v0);
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(bonecolor);
+ surface_tool->add_vertex(point);
+
+ bones[0]=parent;
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(bonecolor);
+ surface_tool->add_vertex(point);
+ bones[0]=i;
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(bonecolor);
+ surface_tool->add_vertex(v1);
+ points[pointidx++]=point;
+
+ }
+
+ }
+
+ SWAP( points[1],points[2] );
+ for(int j=0;j<4;j++) {
+
+
+ bones[0]=parent;
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(bonecolor);
+ surface_tool->add_vertex(points[j]);
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(bonecolor);
+ surface_tool->add_vertex(points[(j+1)%4]);
+ }
+
+
+/*
+ bones[0]=parent;
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(Color(0.4,1,0.4,0.4));
+ surface_tool->add_vertex(v0);
+ bones[0]=i;
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(Color(0.4,1,0.4,0.4));
+ surface_tool->add_vertex(v1);
+*/
+ } else {
+
+ grests[i]=skel->get_bone_rest(i);
+ bones[0]=i;
+ }
+/*
+ Transform t = grests[i];
+ t.orthonormalize();
+
+ for (int i=0;i<6;i++) {
+
+
+ Vector3 face_points[4];
+
+ for (int j=0;j<4;j++) {
+
+ float v[3];
+ v[0]=1.0;
+ v[1]=1-2*((j>>1)&1);
+ v[2]=v[1]*(1-2*(j&1));
+
+ for (int k=0;k<3;k++) {
+
+ if (i<3)
+ face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ else
+ face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ }
+ }
+
+ for(int j=0;j<4;j++) {
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(Color(1.0,0.4,0.4,0.4));
+ surface_tool->add_vertex(t.xform(face_points[j]*0.04));
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(Color(1.0,0.4,0.4,0.4));
+ surface_tool->add_vertex(t.xform(face_points[(j+1)%4]*0.04));
+ }
+
+ }
+ */
+ }
+
+ Ref<Mesh> m = surface_tool->commit();
+ add_mesh(m,false,skel->get_skeleton());
+
+}
+
+SkeletonSpatialGizmo::SkeletonSpatialGizmo(Skeleton* p_skel) {
+
+ skel=p_skel;
+ set_spatial_node(p_skel);
+}
+
+/////
+
+
+void SpatialPlayerSpatialGizmo::redraw() {
+
+ clear();
+ if (splayer->cast_to<SpatialStreamPlayer>()) {
+
+ add_unscaled_billboard(SpatialEditorGizmos::singleton->stream_player_icon,0.05);
+
+ } else if (splayer->cast_to<SpatialSamplePlayer>()) {
+
+ add_unscaled_billboard(SpatialEditorGizmos::singleton->sample_player_icon,0.05);
+
+ }
+
+}
+
+SpatialPlayerSpatialGizmo::SpatialPlayerSpatialGizmo(SpatialPlayer* p_splayer){
+
+ set_spatial_node(p_splayer);
+ splayer=p_splayer;
+}
+
+
+/////
+
+
+void RoomSpatialGizmo::redraw() {
+
+ clear();
+ Ref<RoomBounds> roomie = room->get_room();
+ if (roomie.is_null())
+ return;
+ DVector<Face3> faces = roomie->get_geometry_hint();
+
+ Vector<Vector3> lines;
+ int fc=faces.size();
+ DVector<Face3>::Read r =faces.read();
+
+ Map<_EdgeKey,Vector3> edge_map;
+
+ for(int i=0;i<fc;i++) {
+
+ Vector3 fn = r[i].get_plane().normal;
+
+ for(int j=0;j<3;j++) {
+
+ _EdgeKey ek;
+ ek.from=r[i].vertex[j].snapped(CMP_EPSILON);
+ ek.to=r[i].vertex[(j+1)%3].snapped(CMP_EPSILON);
+ if (ek.from<ek.to)
+ SWAP(ek.from,ek.to);
+
+ Map<_EdgeKey,Vector3>::Element *E=edge_map.find(ek);
+
+ if (E) {
+
+ if (E->get().dot(fn) >0.9) {
+
+ E->get()=Vector3();
+ }
+
+ } else {
+
+ edge_map[ek]=fn;
+ }
+
+ }
+ }
+
+ for(Map<_EdgeKey,Vector3>::Element *E=edge_map.front();E;E=E->next()) {
+
+ if (E->get()!=Vector3()) {
+ lines.push_back(E->key().from);
+ lines.push_back(E->key().to);
+ }
+ }
+
+ add_lines(lines,SpatialEditorGizmos::singleton->room_material);
+ add_collision_segments(lines);
+
+}
+
+RoomSpatialGizmo::RoomSpatialGizmo(Room* p_room){
+
+ set_spatial_node(p_room);
+ room=p_room;
+}
+
+/////
+
+
+void PortalSpatialGizmo::redraw() {
+
+ clear();
+
+ Vector<Point2> points = portal->get_shape();
+ if (points.size()==0) {
+ return;
+ }
+
+ Vector<Vector3> lines;
+
+ Vector3 center;
+ for(int i=0;i<points.size();i++) {
+
+ Vector3 f;
+ f.x=points[i].x;
+ f.y=points[i].y;
+ Vector3 fn;
+ fn.x=points[(i+1)%points.size()].x;
+ fn.y=points[(i+1)%points.size()].y;
+ center+=f;
+
+ lines.push_back(f);
+ lines.push_back(fn);
+ }
+
+ center/=points.size();
+ lines.push_back(center);
+ lines.push_back(center+Vector3(0,0,1));
+
+ add_lines(lines,SpatialEditorGizmos::singleton->portal_material);
+ add_collision_segments(lines);
+
+}
+
+PortalSpatialGizmo::PortalSpatialGizmo(Portal* p_portal){
+
+ set_spatial_node(p_portal);
+ portal=p_portal;
+}
+
+/////
+
+
+void RayCastSpatialGizmo::redraw() {
+
+ clear();
+
+
+ Vector<Vector3> lines;
+
+ lines.push_back(Vector3());
+ lines.push_back(raycast->get_cast_to());
+
+ add_lines(lines,SpatialEditorGizmos::singleton->raycast_material);
+ add_collision_segments(lines);
+
+}
+
+RayCastSpatialGizmo::RayCastSpatialGizmo(RayCast* p_raycast){
+
+ set_spatial_node(p_raycast);
+ raycast=p_raycast;
+}
+
+
+
+/////
+
+
+void VehicleWheelSpatialGizmo::redraw() {
+
+ clear();
+
+
+ Vector<Vector3> points;
+
+ float r = car_wheel->get_radius();
+ const int skip=10;
+ for(int i=0;i<=360;i+=skip) {
+
+ float ra=Math::deg2rad(i);
+ float rb=Math::deg2rad(i+skip);
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
+
+ points.push_back(Vector3(0,a.x,a.y));
+ points.push_back(Vector3(0,b.x,b.y));
+
+ const int springsec=4;
+
+ for(int j=0;j<springsec;j++) {
+ float t = car_wheel->get_suspension_rest_length()*5;
+ points.push_back(Vector3(a.x,i/360.0*t/springsec+j*(t/springsec),a.y)*0.2);
+ points.push_back(Vector3(b.x,(i+skip)/360.0*t/springsec+j*(t/springsec),b.y)*0.2);
+ }
+
+
+ }
+
+ //travel
+ points.push_back(Vector3(0,0,0));
+ points.push_back(Vector3(0,car_wheel->get_suspension_rest_length(),0));
+
+ //axis
+ points.push_back(Vector3(r*0.2,car_wheel->get_suspension_rest_length(),0));
+ points.push_back(Vector3(-r*0.2,car_wheel->get_suspension_rest_length(),0));
+ //axis
+ points.push_back(Vector3(r*0.2,0,0));
+ points.push_back(Vector3(-r*0.2,0,0));
+
+ //forward line
+ points.push_back(Vector3(0,-r,0));
+ points.push_back(Vector3(0,-r,r*2));
+ points.push_back(Vector3(0,-r,r*2));
+ points.push_back(Vector3(r*2*0.2,-r,r*2*0.8));
+ points.push_back(Vector3(0,-r,r*2));
+ points.push_back(Vector3(-r*2*0.2,-r,r*2*0.8));
+
+ add_lines(points,SpatialEditorGizmos::singleton->car_wheel_material);
+ add_collision_segments(points);
+
+}
+
+VehicleWheelSpatialGizmo::VehicleWheelSpatialGizmo(VehicleWheel* p_car_wheel){
+
+ set_spatial_node(p_car_wheel);
+ car_wheel=p_car_wheel;
+}
+
+
+
+///
+
+void TestCubeSpatialGizmo::redraw() {
+
+ clear();
+ add_collision_triangles(SpatialEditorGizmos::singleton->test_cube_tm);
+}
+
+TestCubeSpatialGizmo::TestCubeSpatialGizmo(TestCube* p_tc) {
+
+ tc=p_tc;
+ set_spatial_node(p_tc);
+}
+
+
+///////////
+
+
+
+
+
+
+String CollisionShapeSpatialGizmo::get_handle_name(int p_idx) const {
+
+ Ref<Shape> s = cs->get_shape();
+ if (s.is_null())
+ return "";
+
+ if (s->cast_to<SphereShape>()) {
+
+ return "Radius";
+ }
+
+ if (s->cast_to<BoxShape>()) {
+
+ return "Extents";
+ }
+
+ if (s->cast_to<CapsuleShape>()) {
+
+ return p_idx==0?"Radius":"Height";
+ }
+
+ if (s->cast_to<RayShape>()) {
+
+ return "Length";
+ }
+
+ return "";
+}
+Variant CollisionShapeSpatialGizmo::get_handle_value(int p_idx) const{
+
+ Ref<Shape> s = cs->get_shape();
+ if (s.is_null())
+ return Variant();
+
+ if (s->cast_to<SphereShape>()) {
+
+ Ref<SphereShape> ss = s;
+ return ss->get_radius();
+ }
+
+ if (s->cast_to<BoxShape>()) {
+
+ Ref<BoxShape> bs = s;
+ return bs->get_extents();
+ }
+
+ if (s->cast_to<CapsuleShape>()) {
+
+ Ref<CapsuleShape> cs = s;
+ return p_idx==0?cs->get_radius():cs->get_height();
+ }
+
+ if (s->cast_to<RayShape>()) {
+
+ Ref<RayShape> cs = s;
+ return cs->get_length();
+ }
+
+ return Variant();
+}
+void CollisionShapeSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){
+ Ref<Shape> s = cs->get_shape();
+ if (s.is_null())
+ return;
+
+ Transform gt = cs->get_global_transform();
+ gt.orthonormalize();
+ Transform gi = gt.affine_inverse();
+
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ Vector3 sg[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)};
+
+ if (s->cast_to<SphereShape>()) {
+
+ Ref<SphereShape> ss = s;
+ Vector3 ra,rb;
+ Geometry::get_closest_points_between_segments(Vector3(),Vector3(4096,0,0),sg[0],sg[1],ra,rb);
+ float d = ra.x;
+ if (d<0.001)
+ d=0.001;
+
+ ss->set_radius(d);
+ }
+
+ if (s->cast_to<RayShape>()) {
+
+ Ref<RayShape> rs = s;
+ Vector3 ra,rb;
+ Geometry::get_closest_points_between_segments(Vector3(),Vector3(0,0,4096),sg[0],sg[1],ra,rb);
+ float d = ra.z;
+ if (d<0.001)
+ d=0.001;
+
+ rs->set_length(d);
+ }
+
+
+ if (s->cast_to<BoxShape>()) {
+
+ Vector3 axis;
+ axis[p_idx]=1.0;
+ Ref<BoxShape> bs = s;
+ Vector3 ra,rb;
+ Geometry::get_closest_points_between_segments(Vector3(),axis*4096,sg[0],sg[1],ra,rb);
+ float d = ra[p_idx];
+ if (d<0.001)
+ d=0.001;
+
+ Vector3 he = bs->get_extents();
+ he[p_idx]=d;
+ bs->set_extents(he);
+
+ }
+
+ if (s->cast_to<CapsuleShape>()) {
+
+ Vector3 axis;
+ axis[p_idx==0?0:2]=1.0;
+ Ref<CapsuleShape> cs = s;
+ Vector3 ra,rb;
+ Geometry::get_closest_points_between_segments(Vector3(),axis*4096,sg[0],sg[1],ra,rb);
+ float d = axis.dot(ra);
+ if (p_idx==1)
+ d-=cs->get_radius();
+ if (d<0.001)
+ d=0.001;
+
+ if (p_idx==0)
+ cs->set_radius(d);
+ else if (p_idx==1)
+ cs->set_height(d*2.0);
+
+ }
+
+}
+void CollisionShapeSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
+ Ref<Shape> s = cs->get_shape();
+ if (s.is_null())
+ return;
+
+ if (s->cast_to<SphereShape>()) {
+
+ Ref<SphereShape> ss=s;
+ if (p_cancel) {
+ ss->set_radius(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ ur->create_action("Change Sphere Shape Radius");
+ ur->add_do_method(ss.ptr(),"set_radius",ss->get_radius());
+ ur->add_undo_method(ss.ptr(),"set_radius",p_restore);
+ ur->commit_action();
+
+ }
+
+ if (s->cast_to<BoxShape>()) {
+
+ Ref<BoxShape> ss=s;
+ if (p_cancel) {
+ ss->set_extents(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ ur->create_action("Change Box Shape Extents");
+ ur->add_do_method(ss.ptr(),"set_extents",ss->get_extents());
+ ur->add_undo_method(ss.ptr(),"set_extents",p_restore);
+ ur->commit_action();
+ }
+
+ if (s->cast_to<CapsuleShape>()) {
+
+ Ref<CapsuleShape> ss=s;
+ if (p_cancel) {
+ if (p_idx==0)
+ ss->set_radius(p_restore);
+ else
+ ss->set_height(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ if (p_idx==0) {
+ ur->create_action("Change Capsule Shape Radius");
+ ur->add_do_method(ss.ptr(),"set_radius",ss->get_radius());
+ ur->add_undo_method(ss.ptr(),"set_radius",p_restore);
+ } else {
+ ur->create_action("Change Capsule Shape Height");
+ ur->add_do_method(ss.ptr(),"set_height",ss->get_height());
+ ur->add_undo_method(ss.ptr(),"set_height",p_restore);
+
+ }
+
+ ur->commit_action();
+
+ }
+
+ if (s->cast_to<RayShape>()) {
+
+ Ref<RayShape> ss=s;
+ if (p_cancel) {
+ ss->set_length(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ ur->create_action("Change Ray Shape Length");
+ ur->add_do_method(ss.ptr(),"set_length",ss->get_length());
+ ur->add_undo_method(ss.ptr(),"set_length",p_restore);
+ ur->commit_action();
+
+ }
+
+}
+void CollisionShapeSpatialGizmo::redraw(){
+
+ clear();
+
+ Ref<Shape> s = cs->get_shape();
+ if (s.is_null())
+ return;
+
+ if (s->cast_to<SphereShape>()) {
+
+ Ref<SphereShape> sp= s;
+ float r=sp->get_radius();
+
+ Vector<Vector3> points;
+
+ for(int i=0;i<=360;i++) {
+
+ float ra=Math::deg2rad(i);
+ float rb=Math::deg2rad(i+1);
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
+
+ points.push_back(Vector3(a.x,0,a.y));
+ points.push_back(Vector3(b.x,0,b.y));
+ points.push_back(Vector3(0,a.x,a.y));
+ points.push_back(Vector3(0,b.x,b.y));
+ points.push_back(Vector3(a.x,a.y,0));
+ points.push_back(Vector3(b.x,b.y,0));
+
+ }
+
+ Vector<Vector3> collision_segments;
+
+ for(int i=0;i<64;i++) {
+
+ float ra=i*Math_PI*2.0/64.0;
+ float rb=(i+1)*Math_PI*2.0/64.0;
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
+
+ collision_segments.push_back(Vector3(a.x,0,a.y));
+ collision_segments.push_back(Vector3(b.x,0,b.y));
+ collision_segments.push_back(Vector3(0,a.x,a.y));
+ collision_segments.push_back(Vector3(0,b.x,b.y));
+ collision_segments.push_back(Vector3(a.x,a.y,0));
+ collision_segments.push_back(Vector3(b.x,b.y,0));
+ }
+
+ add_lines(points,SpatialEditorGizmos::singleton->shape_material);
+ add_collision_segments(collision_segments);
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(r,0,0));
+ add_handles(handles);
+
+ }
+
+ if (s->cast_to<BoxShape>()) {
+
+ Ref<BoxShape> bs=s;
+ Vector<Vector3> lines;
+ AABB aabb;
+ aabb.pos=-bs->get_extents();
+ aabb.size=aabb.pos*-2;
+
+ for(int i=0;i<12;i++) {
+ Vector3 a,b;
+ aabb.get_edge(i,a,b);
+ lines.push_back(a);
+ lines.push_back(b);
+ }
+
+ Vector<Vector3> handles;
+
+ for(int i=0;i<3;i++) {
+
+ Vector3 ax;
+ ax[i]=bs->get_extents()[i];
+ handles.push_back(ax);
+ }
+
+ add_lines(lines,SpatialEditorGizmos::singleton->shape_material);
+ add_collision_segments(lines);
+ add_handles(handles);
+
+ }
+
+ if (s->cast_to<CapsuleShape>()) {
+
+ Ref<CapsuleShape> cs=s;
+ float radius = cs->get_radius();
+ float height = cs->get_height();
+
+
+ Vector<Vector3> points;
+
+ Vector3 d(0,0,height*0.5);
+ for(int i=0;i<360;i++) {
+
+ float ra=Math::deg2rad(i);
+ float rb=Math::deg2rad(i+1);
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*radius;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*radius;
+
+ points.push_back(Vector3(a.x,a.y,0)+d);
+ points.push_back(Vector3(b.x,b.y,0)+d);
+
+ points.push_back(Vector3(a.x,a.y,0)-d);
+ points.push_back(Vector3(b.x,b.y,0)-d);
+
+ if (i%90==0) {
+
+ points.push_back(Vector3(a.x,a.y,0)+d);
+ points.push_back(Vector3(a.x,a.y,0)-d);
+ }
+
+ Vector3 dud = i<180?d:-d;
+
+ points.push_back(Vector3(0,a.y,a.x)+dud);
+ points.push_back(Vector3(0,b.y,b.x)+dud);
+ points.push_back(Vector3(a.y,0,a.x)+dud);
+ points.push_back(Vector3(b.y,0,b.x)+dud);
+
+ }
+
+ add_lines(points,SpatialEditorGizmos::singleton->shape_material);
+
+ Vector<Vector3> collision_segments;
+
+ for(int i=0;i<64;i++) {
+
+ float ra=i*Math_PI*2.0/64.0;
+ float rb=(i+1)*Math_PI*2.0/64.0;
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*radius;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*radius;
+
+ collision_segments.push_back(Vector3(a.x,a.y,0)+d);
+ collision_segments.push_back(Vector3(b.x,b.y,0)+d);
+
+ collision_segments.push_back(Vector3(a.x,a.y,0)-d);
+ collision_segments.push_back(Vector3(b.x,b.y,0)-d);
+
+ if (i%16==0) {
+
+ collision_segments.push_back(Vector3(a.x,a.y,0)+d);
+ collision_segments.push_back(Vector3(a.x,a.y,0)-d);
+ }
+
+ Vector3 dud = i<32?d:-d;
+
+ collision_segments.push_back(Vector3(0,a.y,a.x)+dud);
+ collision_segments.push_back(Vector3(0,b.y,b.x)+dud);
+ collision_segments.push_back(Vector3(a.y,0,a.x)+dud);
+ collision_segments.push_back(Vector3(b.y,0,b.x)+dud);
+
+ }
+
+ add_collision_segments(collision_segments);
+
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(cs->get_radius(),0,0));
+ handles.push_back(Vector3(0,0,cs->get_height()*0.5+cs->get_radius()));
+ add_handles(handles);
+
+
+ }
+
+ if (s->cast_to<PlaneShape>()) {
+
+ Ref<PlaneShape> ps=s;
+ Plane p = ps->get_plane();
+ Vector<Vector3> points;
+
+ Vector3 n1 = p.get_any_perpendicular_normal();
+ Vector3 n2 = p.normal.cross(n1).normalized();
+
+ Vector3 pface[4]={
+ p.normal*p.d+n1*10.0+n2*10.0,
+ p.normal*p.d+n1*10.0+n2*-10.0,
+ p.normal*p.d+n1*-10.0+n2*-10.0,
+ p.normal*p.d+n1*-10.0+n2*10.0,
+ };
+
+ points.push_back(pface[0]);
+ points.push_back(pface[1]);
+ points.push_back(pface[1]);
+ points.push_back(pface[2]);
+ points.push_back(pface[2]);
+ points.push_back(pface[3]);
+ points.push_back(pface[3]);
+ points.push_back(pface[0]);
+ points.push_back(p.normal*p.d);
+ points.push_back(p.normal*p.d+p.normal*3);
+
+ add_lines(points,SpatialEditorGizmos::singleton->shape_material);
+ add_collision_segments(points);
+
+ }
+
+
+ if (s->cast_to<ConvexPolygonShape>()) {
+
+ DVector<Vector3> points = s->cast_to<ConvexPolygonShape>()->get_points();
+
+ if (points.size()>3) {
+
+ QuickHull qh;
+ Vector<Vector3> varr = Variant(points);
+ Geometry::MeshData md;
+ Error err = qh.build(varr,md);
+ if (err==OK) {
+ Vector<Vector3> points;
+ points.resize(md.edges.size()*2);
+ for(int i=0;i<md.edges.size();i++) {
+ points[i*2+0]=md.vertices[md.edges[i].a];
+ points[i*2+1]=md.vertices[md.edges[i].b];
+ }
+
+
+ add_lines(points,SpatialEditorGizmos::singleton->shape_material);
+ add_collision_segments(points);
+
+ }
+ }
+
+ }
+
+
+ if (s->cast_to<RayShape>()) {
+
+ Ref<RayShape> rs=s;
+
+ Vector<Vector3> points;
+ points.push_back(Vector3());
+ points.push_back(Vector3(0,0,rs->get_length()));
+ add_lines(points,SpatialEditorGizmos::singleton->shape_material);
+ add_collision_segments(points);
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(0,0,rs->get_length()));
+ add_handles(handles);
+
+
+ }
+
+}
+CollisionShapeSpatialGizmo::CollisionShapeSpatialGizmo(CollisionShape* p_cs) {
+
+ cs=p_cs;
+ set_spatial_node(p_cs);
+}
+
+
+
+/////
+
+
+void CollisionPolygonSpatialGizmo::redraw() {
+
+ clear();
+
+ Vector<Vector2> points = polygon->get_polygon();
+ float depth = polygon->get_depth()*0.5;
+
+ Vector<Vector3> lines;
+ for(int i=0;i<points.size();i++) {
+
+ int n = (i+1)%points.size();
+ lines.push_back(Vector3(points[i].x,points[i].y,depth));
+ lines.push_back(Vector3(points[n].x,points[n].y,depth));
+ lines.push_back(Vector3(points[i].x,points[i].y,-depth));
+ lines.push_back(Vector3(points[n].x,points[n].y,-depth));
+ lines.push_back(Vector3(points[i].x,points[i].y,depth));
+ lines.push_back(Vector3(points[i].x,points[i].y,-depth));
+
+ }
+
+ add_lines(lines,SpatialEditorGizmos::singleton->shape_material);
+ add_collision_segments(lines);
+}
+
+CollisionPolygonSpatialGizmo::CollisionPolygonSpatialGizmo(CollisionPolygon* p_polygon){
+
+ set_spatial_node(p_polygon);
+ polygon=p_polygon;
+}
+///
+
+
+String VisibilityNotifierGizmo::get_handle_name(int p_idx) const {
+
+ switch(p_idx) {
+ case 0: return "X";
+ case 1: return "Y";
+ case 2: return "Z";
+ }
+
+ return "";
+}
+Variant VisibilityNotifierGizmo::get_handle_value(int p_idx) const{
+
+ return notifier->get_aabb();
+}
+void VisibilityNotifierGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){
+
+
+ Transform gt = notifier->get_global_transform();
+ //gt.orthonormalize();
+ Transform gi = gt.affine_inverse();
+
+ AABB aabb = notifier->get_aabb();
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ Vector3 sg[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)};
+ Vector3 ofs = aabb.pos+aabb.size*0.5;;
+
+ Vector3 axis;
+ axis[p_idx]=1.0;
+
+ Vector3 ra,rb;
+ Geometry::get_closest_points_between_segments(ofs,ofs+axis*4096,sg[0],sg[1],ra,rb);
+ float d = ra[p_idx];
+ if (d<0.001)
+ d=0.001;
+
+ Vector3 he = aabb.size;
+ aabb.pos[p_idx]=(aabb.pos[p_idx]+aabb.size[p_idx]*0.5)-d;
+ aabb.size[p_idx]=d*2;
+ notifier->set_aabb(aabb);
+}
+
+void VisibilityNotifierGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
+
+
+ if (p_cancel) {
+ notifier->set_aabb(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ ur->create_action("Change Notifier Extents");
+ ur->add_do_method(notifier,"set_aabb",notifier->get_aabb());
+ ur->add_undo_method(notifier,"set_aabb",p_restore);
+ ur->commit_action();
+
+}
+
+void VisibilityNotifierGizmo::redraw(){
+
+ clear();
+
+ Vector<Vector3> lines;
+ AABB aabb = notifier->get_aabb();
+
+ for(int i=0;i<12;i++) {
+ Vector3 a,b;
+ aabb.get_edge(i,a,b);
+ lines.push_back(a);
+ lines.push_back(b);
+ }
+
+ Vector<Vector3> handles;
+
+
+ for(int i=0;i<3;i++) {
+
+ Vector3 ax;
+ ax[i]=aabb.pos[i]+aabb.size[i];
+ handles.push_back(ax);
+ }
+
+ add_lines(lines,SpatialEditorGizmos::singleton->visibility_notifier_material);
+ //add_unscaled_billboard(SpatialEditorGizmos::singleton->visi,0.05);
+ add_collision_segments(lines);
+ add_handles(handles);
+
+}
+VisibilityNotifierGizmo::VisibilityNotifierGizmo(VisibilityNotifier* p_notifier){
+
+ notifier=p_notifier;
+ set_spatial_node(p_notifier);
+}
+
+////////
+
+
+
+void NavigationMeshSpatialGizmo::redraw() {
+
+ clear();
+ Ref<NavigationMesh> navmeshie = navmesh->get_navigation_mesh();
+ if (navmeshie.is_null())
+ return;
+
+ DVector<Vector3> vertices = navmeshie->get_vertices();
+ DVector<Vector3>::Read vr=vertices.read();
+ List<Face3> faces;
+ for(int i=0;i<navmeshie->get_polygon_count();i++) {
+ Vector<int> p = navmeshie->get_polygon(i);
+
+ for(int j=2;j<p.size();j++) {
+ Face3 f;
+ f.vertex[0]=vr[p[0]];
+ f.vertex[1]=vr[p[j-1]];
+ f.vertex[2]=vr[p[j]];
+
+ faces.push_back(f);
+ }
+ }
+
+
+ Map<_EdgeKey,bool> edge_map;
+ DVector<Vector3> tmeshfaces;
+ tmeshfaces.resize(faces.size()*3);
+
+ {
+ DVector<Vector3>::Write tw=tmeshfaces.write();
+ int tidx=0;
+
+
+ for(List<Face3>::Element *E=faces.front();E;E=E->next()) {
+
+ const Face3 &f = E->get();
+
+ for(int j=0;j<3;j++) {
+
+ tw[tidx++]=f.vertex[j];
+ _EdgeKey ek;
+ ek.from=f.vertex[j].snapped(CMP_EPSILON);
+ ek.to=f.vertex[(j+1)%3].snapped(CMP_EPSILON);
+ if (ek.from<ek.to)
+ SWAP(ek.from,ek.to);
+
+ Map<_EdgeKey,bool>::Element *E=edge_map.find(ek);
+
+ if (E) {
+
+ E->get()=false;
+
+ } else {
+
+ edge_map[ek]=true;
+ }
+
+ }
+ }
+ }
+ Vector<Vector3> lines;
+
+ for(Map<_EdgeKey,bool>::Element *E=edge_map.front();E;E=E->next()) {
+
+ if (E->get()) {
+ lines.push_back(E->key().from);
+ lines.push_back(E->key().to);
+ }
+ }
+
+ Ref<TriangleMesh> tmesh = memnew( TriangleMesh);
+ tmesh->create(tmeshfaces);
+
+ if (lines.size())
+ add_lines(lines,navmesh->is_enabled()?SpatialEditorGizmos::singleton->navmesh_edge_material:SpatialEditorGizmos::singleton->navmesh_edge_material_disabled);
+ add_collision_triangles(tmesh);
+ Ref<Mesh> m = memnew( Mesh );
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+ a[0]=tmeshfaces;
+ m->add_surface(Mesh::PRIMITIVE_TRIANGLES,a);
+ m->surface_set_material(0,navmesh->is_enabled()?SpatialEditorGizmos::singleton->navmesh_solid_material:SpatialEditorGizmos::singleton->navmesh_solid_material_disabled);
+ add_mesh(m);
+ add_collision_segments(lines);
+
+}
+
+NavigationMeshSpatialGizmo::NavigationMeshSpatialGizmo(NavigationMeshInstance *p_navmesh){
+
+ set_spatial_node(p_navmesh);
+ navmesh=p_navmesh;
+}
+
+//////
+///
+///
+
+
+
+void PinJointSpatialGizmo::redraw() {
+
+ clear();
+ Vector<Vector3> cursor_points;
+ float cs = 0.25;
+ cursor_points.push_back(Vector3(+cs,0,0));
+ cursor_points.push_back(Vector3(-cs,0,0));
+ cursor_points.push_back(Vector3(0,+cs,0));
+ cursor_points.push_back(Vector3(0,-cs,0));
+ cursor_points.push_back(Vector3(0,0,+cs));
+ cursor_points.push_back(Vector3(0,0,-cs));
+ add_collision_segments(cursor_points);
+ add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
+
+}
+
+
+PinJointSpatialGizmo::PinJointSpatialGizmo(PinJoint* p_p3d) {
+
+ p3d=p_p3d;
+ set_spatial_node(p3d);
+}
+
+////
+
+void HingeJointSpatialGizmo::redraw() {
+
+ clear();
+ Vector<Vector3> cursor_points;
+ float cs = 0.25;
+ /*cursor_points.push_back(Vector3(+cs,0,0));
+ cursor_points.push_back(Vector3(-cs,0,0));
+ cursor_points.push_back(Vector3(0,+cs,0));
+ cursor_points.push_back(Vector3(0,-cs,0));*/
+ cursor_points.push_back(Vector3(0,0,+cs*2));
+ cursor_points.push_back(Vector3(0,0,-cs*2));
+
+ float ll = p3d->get_param(HingeJoint::PARAM_LIMIT_LOWER);
+ float ul = p3d->get_param(HingeJoint::PARAM_LIMIT_UPPER);
+
+ if (p3d->get_flag(HingeJoint::FLAG_USE_LIMIT) && ll<ul) {
+
+ const int points = 32;
+ float step = (ul-ll)/points;
+
+
+ for(int i=0;i<points;i++) {
+
+ float s = ll+i*(ul-ll)/points;
+ float n = ll+(i+1)*(ul-ll)/points;
+
+ Vector3 from=Vector3( -Math::sin(s),Math::cos(s), 0 )*cs;
+ Vector3 to=Vector3( -Math::sin(n),Math::cos(n), 0 )*cs;
+
+ if (i==points-1) {
+ cursor_points.push_back(to);
+ cursor_points.push_back(Vector3());
+ }
+ if (i==0) {
+ cursor_points.push_back(from);
+ cursor_points.push_back(Vector3());
+ }
+
+
+ cursor_points.push_back(from);
+ cursor_points.push_back(to);
+
+
+ }
+
+ cursor_points.push_back(Vector3(0,cs*1.5,0));
+ cursor_points.push_back(Vector3());
+
+ } else {
+
+
+ const int points = 32;
+
+ for(int i=0;i<points;i++) {
+
+ float s = ll+i*(Math_PI*2.0)/points;
+ float n = ll+(i+1)*(Math_PI*2.0)/points;
+
+ Vector3 from=Vector3( -Math::sin(s),Math::cos(s), 0 )*cs;
+ Vector3 to=Vector3( -Math::sin(n),Math::cos(n), 0 )*cs;
+
+ cursor_points.push_back(from);
+ cursor_points.push_back(to);
+
+ }
+
+ }
+ add_collision_segments(cursor_points);
+ add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
+
+}
+
+
+HingeJointSpatialGizmo::HingeJointSpatialGizmo(HingeJoint* p_p3d) {
+
+ p3d=p_p3d;
+ set_spatial_node(p3d);
+}
+
+///////
+///
+////
+
+void SliderJointSpatialGizmo::redraw() {
+
+ clear();
+ Vector<Vector3> cursor_points;
+ float cs = 0.25;
+ /*cursor_points.push_back(Vector3(+cs,0,0));
+ cursor_points.push_back(Vector3(-cs,0,0));
+ cursor_points.push_back(Vector3(0,+cs,0));
+ cursor_points.push_back(Vector3(0,-cs,0));*/
+ cursor_points.push_back(Vector3(0,0,+cs*2));
+ cursor_points.push_back(Vector3(0,0,-cs*2));
+
+ float ll = p3d->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_LOWER);
+ float ul = p3d->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_UPPER);
+ float lll = -p3d->get_param(SliderJoint::PARAM_LINEAR_LIMIT_LOWER);
+ float lul = -p3d->get_param(SliderJoint::PARAM_LINEAR_LIMIT_UPPER);
+
+ if (lll>lul) {
+
+ cursor_points.push_back(Vector3(lul,0,0));
+ cursor_points.push_back(Vector3(lll,0,0));
+
+ cursor_points.push_back(Vector3(lul,-cs,-cs));
+ cursor_points.push_back(Vector3(lul,-cs,cs));
+ cursor_points.push_back(Vector3(lul,-cs,cs));
+ cursor_points.push_back(Vector3(lul,cs,cs));
+ cursor_points.push_back(Vector3(lul,cs,cs));
+ cursor_points.push_back(Vector3(lul,cs,-cs));
+ cursor_points.push_back(Vector3(lul,cs,-cs));
+ cursor_points.push_back(Vector3(lul,-cs,-cs));
+
+
+ cursor_points.push_back(Vector3(lll,-cs,-cs));
+ cursor_points.push_back(Vector3(lll,-cs,cs));
+ cursor_points.push_back(Vector3(lll,-cs,cs));
+ cursor_points.push_back(Vector3(lll,cs,cs));
+ cursor_points.push_back(Vector3(lll,cs,cs));
+ cursor_points.push_back(Vector3(lll,cs,-cs));
+ cursor_points.push_back(Vector3(lll,cs,-cs));
+ cursor_points.push_back(Vector3(lll,-cs,-cs));
+
+
+ } else {
+
+ cursor_points.push_back(Vector3(+cs*2,0,0));
+ cursor_points.push_back(Vector3(-cs*2,0,0));
+
+ }
+
+ if (ll<ul) {
+
+ const int points = 32;
+ float step = (ul-ll)/points;
+
+
+ for(int i=0;i<points;i++) {
+
+ float s = ll+i*(ul-ll)/points;
+ float n = ll+(i+1)*(ul-ll)/points;
+
+ Vector3 from=Vector3(0, Math::cos(s), -Math::sin(s) )*cs;
+ Vector3 to=Vector3(0,Math::cos(n), -Math::sin(n) )*cs;
+
+ if (i==points-1) {
+ cursor_points.push_back(to);
+ cursor_points.push_back(Vector3());
+ }
+ if (i==0) {
+ cursor_points.push_back(from);
+ cursor_points.push_back(Vector3());
+ }
+
+
+ cursor_points.push_back(from);
+ cursor_points.push_back(to);
+
+
+ }
+
+ cursor_points.push_back(Vector3(0,cs*1.5,0));
+ cursor_points.push_back(Vector3());
+
+ } else {
+
+
+ const int points = 32;
+
+ for(int i=0;i<points;i++) {
+
+ float s = ll+i*(Math_PI*2.0)/points;
+ float n = ll+(i+1)*(Math_PI*2.0)/points;
+
+ Vector3 from=Vector3(0,Math::cos(s),-Math::sin(s) )*cs;
+ Vector3 to=Vector3( 0,Math::cos(n),-Math::sin(n) )*cs;
+
+ cursor_points.push_back(from);
+ cursor_points.push_back(to);
+
+ }
+
+ }
+ add_collision_segments(cursor_points);
+ add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
+
+}
+
+
+SliderJointSpatialGizmo::SliderJointSpatialGizmo(SliderJoint* p_p3d) {
+
+ p3d=p_p3d;
+ set_spatial_node(p3d);
+}
+
+///////
+///
+////
+
+void ConeTwistJointSpatialGizmo::redraw() {
+
+ clear();
+ float cs = 0.25;
+ Vector<Vector3> points;
+
+ float r = 1.0;
+ float w = r*Math::sin(p3d->get_param(ConeTwistJoint::PARAM_SWING_SPAN));
+ float d = r*Math::cos(p3d->get_param(ConeTwistJoint::PARAM_SWING_SPAN));
+
+
+ //swing
+ for(int i=0;i<360;i+=10) {
+
+ float ra=Math::deg2rad(i);
+ float rb=Math::deg2rad(i+10);
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w;
+
+ /*points.push_back(Vector3(a.x,0,a.y));
+ points.push_back(Vector3(b.x,0,b.y));
+ points.push_back(Vector3(0,a.x,a.y));
+ points.push_back(Vector3(0,b.x,b.y));*/
+ points.push_back(Vector3(d,a.x,a.y));
+ points.push_back(Vector3(d,b.x,b.y));
+
+ if (i%90==0) {
+
+ points.push_back(Vector3(d,a.x,a.y));
+ points.push_back(Vector3());
+
+ }
+ }
+
+ points.push_back(Vector3());
+ points.push_back(Vector3(1,0,0));
+
+ //twist
+ /*
+ */
+ float ts=Math::rad2deg(p3d->get_param(ConeTwistJoint::PARAM_TWIST_SPAN));
+ ts=MIN(ts,720);
+
+
+ for(int i=0;i<int(ts);i+=5) {
+
+ float ra=Math::deg2rad(i);
+ float rb=Math::deg2rad(i+5);
+ float c = i/720.0;
+ float cn = (i+5)/720.0;
+ Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w*c;
+ Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w*cn;
+
+ /*points.push_back(Vector3(a.x,0,a.y));
+ points.push_back(Vector3(b.x,0,b.y));
+ points.push_back(Vector3(0,a.x,a.y));
+ points.push_back(Vector3(0,b.x,b.y));*/
+
+ points.push_back(Vector3(c,a.x,a.y));
+ points.push_back(Vector3(cn,b.x,b.y));
+
+ }
+
+
+ add_collision_segments(points);
+ add_lines(points,SpatialEditorGizmos::singleton->joint_material);
+
+}
+
+
+ConeTwistJointSpatialGizmo::ConeTwistJointSpatialGizmo(ConeTwistJoint* p_p3d) {
+
+ p3d=p_p3d;
+ set_spatial_node(p3d);
+}
+
+////////
+/// \brief SpatialEditorGizmos::singleton
+///
+///////
+///
+////
+
+void Generic6DOFJointSpatialGizmo::redraw() {
+
+ clear();
+ Vector<Vector3> cursor_points;
+ float cs = 0.25;
+
+ for(int ax=0;ax<3;ax++) {
+ /*cursor_points.push_back(Vector3(+cs,0,0));
+ cursor_points.push_back(Vector3(-cs,0,0));
+ cursor_points.push_back(Vector3(0,+cs,0));
+ cursor_points.push_back(Vector3(0,-cs,0));
+ cursor_points.push_back(Vector3(0,0,+cs*2));
+ cursor_points.push_back(Vector3(0,0,-cs*2)); */
+
+ float ll;
+ float ul;
+ float lll;
+ float lul;
+
+ int a1,a2,a3;
+ bool enable_ang;
+ bool enable_lin;
+
+ switch(ax) {
+ case 0:
+ ll = p3d->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT);
+ ul = p3d->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT);
+ lll = -p3d->get_param_x(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT);
+ lul = -p3d->get_param_x(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT);
+ enable_ang = p3d->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT);
+ enable_lin = p3d->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT);
+ a1=0;
+ a2=1;
+ a3=2;
+ break;
+ case 1:
+ ll = p3d->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT);
+ ul = p3d->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT);
+ lll = -p3d->get_param_y(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT);
+ lul = -p3d->get_param_y(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT);
+ enable_ang = p3d->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT);
+ enable_lin = p3d->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT);
+ a1=2;
+ a2=0;
+ a3=1;
+ break;
+ case 2:
+ ll = p3d->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT);
+ ul = p3d->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT);
+ lll = -p3d->get_param_z(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT);
+ lul = -p3d->get_param_z(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT);
+ enable_ang = p3d->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT);
+ enable_lin = p3d->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT);
+
+ a1=1;
+ a2=2;
+ a3=0;
+ break;
+ }
+
+#define ADD_VTX(x,y,z)\
+ {\
+ Vector3 v;\
+ v[a1]=(x);\
+ v[a2]=(y);\
+ v[a3]=(z);\
+ cursor_points.push_back(v);\
+ }
+
+#define SET_VTX(what,x,y,z)\
+ {\
+ Vector3 v;\
+ v[a1]=(x);\
+ v[a2]=(y);\
+ v[a3]=(z);\
+ what=v;\
+ }
+
+
+
+
+ if (enable_lin && lll>=lul) {
+
+ ADD_VTX(lul,0,0);
+ ADD_VTX(lll,0,0);
+
+ ADD_VTX(lul,-cs,-cs);
+ ADD_VTX(lul,-cs,cs);
+ ADD_VTX(lul,-cs,cs);
+ ADD_VTX(lul,cs,cs);
+ ADD_VTX(lul,cs,cs);
+ ADD_VTX(lul,cs,-cs);
+ ADD_VTX(lul,cs,-cs);
+ ADD_VTX(lul,-cs,-cs);
+
+
+ ADD_VTX(lll,-cs,-cs);
+ ADD_VTX(lll,-cs,cs);
+ ADD_VTX(lll,-cs,cs);
+ ADD_VTX(lll,cs,cs);
+ ADD_VTX(lll,cs,cs);
+ ADD_VTX(lll,cs,-cs);
+ ADD_VTX(lll,cs,-cs);
+ ADD_VTX(lll,-cs,-cs);
+
+
+ } else {
+
+ ADD_VTX(+cs*2,0,0);
+ ADD_VTX(-cs*2,0,0);
+
+ }
+
+ if (enable_ang && ll<=ul) {
+
+ const int points = 32;
+ float step = (ul-ll)/points;
+
+
+ for(int i=0;i<points;i++) {
+
+ float s = ll+i*(ul-ll)/points;
+ float n = ll+(i+1)*(ul-ll)/points;
+
+ Vector3 from;
+ SET_VTX(from,0, Math::cos(s), -Math::sin(s) );
+ from*=cs;
+ Vector3 to;
+ SET_VTX(to,0,Math::cos(n), -Math::sin(n));
+ to*=cs;
+
+ if (i==points-1) {
+ cursor_points.push_back(to);
+ cursor_points.push_back(Vector3());
+ }
+ if (i==0) {
+ cursor_points.push_back(from);
+ cursor_points.push_back(Vector3());
+ }
+
+
+ cursor_points.push_back(from);
+ cursor_points.push_back(to);
+
+
+ }
+
+ ADD_VTX(0,cs*1.5,0);
+ cursor_points.push_back(Vector3());
+
+ } else {
+
+
+ const int points = 32;
+
+ for(int i=0;i<points;i++) {
+
+ float s = ll+i*(Math_PI*2.0)/points;
+ float n = ll+(i+1)*(Math_PI*2.0)/points;
+
+// Vector3 from=Vector3(0,Math::cos(s),-Math::sin(s) )*cs;
+// Vector3 to=Vector3( 0,Math::cos(n),-Math::sin(n) )*cs;
+
+ Vector3 from;
+ SET_VTX(from,0, Math::cos(s), -Math::sin(s) );
+ from*=cs;
+ Vector3 to;
+ SET_VTX(to,0,Math::cos(n), -Math::sin(n));
+ to*=cs;
+
+ cursor_points.push_back(from);
+ cursor_points.push_back(to);
+
+ }
+
+ }
+ }
+
+#undef ADD_VTX
+#undef SET_VTX
+
+ add_collision_segments(cursor_points);
+ add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
+
+}
+
+
+Generic6DOFJointSpatialGizmo::Generic6DOFJointSpatialGizmo(Generic6DOFJoint* p_p3d) {
+
+ p3d=p_p3d;
+ set_spatial_node(p3d);
+}
+
+///////
+///
+////
+
+
+SpatialEditorGizmos *SpatialEditorGizmos::singleton=NULL;
+
+Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
+
+ if (p_spatial->cast_to<Light>()) {
+
+ Ref<LightSpatialGizmo> lsg = memnew( LightSpatialGizmo(p_spatial->cast_to<Light>()) );
+ return lsg;
+ }
+
+ if (p_spatial->cast_to<Camera>()) {
+
+ Ref<CameraSpatialGizmo> lsg = memnew( CameraSpatialGizmo(p_spatial->cast_to<Camera>()) );
+ return lsg;
+ }
+
+ if (p_spatial->cast_to<Skeleton>()) {
+
+ Ref<SkeletonSpatialGizmo> lsg = memnew( SkeletonSpatialGizmo(p_spatial->cast_to<Skeleton>()) );
+ return lsg;
+ }
+
+
+ if (p_spatial->cast_to<Position3D>()) {
+
+ Ref<Position3DSpatialGizmo> lsg = memnew( Position3DSpatialGizmo(p_spatial->cast_to<Position3D>()) );
+ return lsg;
+ }
+
+ if (p_spatial->cast_to<MeshInstance>()) {
+
+ Ref<MeshInstanceSpatialGizmo> misg = memnew( MeshInstanceSpatialGizmo(p_spatial->cast_to<MeshInstance>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<Room>()) {
+
+ Ref<RoomSpatialGizmo> misg = memnew( RoomSpatialGizmo(p_spatial->cast_to<Room>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<NavigationMeshInstance>()) {
+
+ Ref<NavigationMeshSpatialGizmo> misg = memnew( NavigationMeshSpatialGizmo(p_spatial->cast_to<NavigationMeshInstance>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<RayCast>()) {
+
+ Ref<RayCastSpatialGizmo> misg = memnew( RayCastSpatialGizmo(p_spatial->cast_to<RayCast>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<Portal>()) {
+
+ Ref<PortalSpatialGizmo> misg = memnew( PortalSpatialGizmo(p_spatial->cast_to<Portal>()) );
+ return misg;
+ }
+
+
+ if (p_spatial->cast_to<TestCube>()) {
+
+ Ref<TestCubeSpatialGizmo> misg = memnew( TestCubeSpatialGizmo(p_spatial->cast_to<TestCube>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<SpatialPlayer>()) {
+
+ Ref<SpatialPlayerSpatialGizmo> misg = memnew( SpatialPlayerSpatialGizmo(p_spatial->cast_to<SpatialPlayer>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<CollisionShape>()) {
+
+ Ref<CollisionShapeSpatialGizmo> misg = memnew( CollisionShapeSpatialGizmo(p_spatial->cast_to<CollisionShape>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<VisibilityNotifier>()) {
+
+ Ref<VisibilityNotifierGizmo> misg = memnew( VisibilityNotifierGizmo(p_spatial->cast_to<VisibilityNotifier>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<VehicleWheel>()) {
+
+ Ref<VehicleWheelSpatialGizmo> misg = memnew( VehicleWheelSpatialGizmo(p_spatial->cast_to<VehicleWheel>()) );
+ return misg;
+ }
+ if (p_spatial->cast_to<PinJoint>()) {
+
+ Ref<PinJointSpatialGizmo> misg = memnew( PinJointSpatialGizmo(p_spatial->cast_to<PinJoint>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<HingeJoint>()) {
+
+ Ref<HingeJointSpatialGizmo> misg = memnew( HingeJointSpatialGizmo(p_spatial->cast_to<HingeJoint>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<SliderJoint>()) {
+
+ Ref<SliderJointSpatialGizmo> misg = memnew( SliderJointSpatialGizmo(p_spatial->cast_to<SliderJoint>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<ConeTwistJoint>()) {
+
+ Ref<ConeTwistJointSpatialGizmo> misg = memnew( ConeTwistJointSpatialGizmo(p_spatial->cast_to<ConeTwistJoint>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<Generic6DOFJoint>()) {
+
+ Ref<Generic6DOFJointSpatialGizmo> misg = memnew( Generic6DOFJointSpatialGizmo(p_spatial->cast_to<Generic6DOFJoint>()) );
+ return misg;
+ }
+
+ if (p_spatial->cast_to<CollisionPolygon>()) {
+
+ Ref<CollisionPolygonSpatialGizmo> misg = memnew( CollisionPolygonSpatialGizmo(p_spatial->cast_to<CollisionPolygon>()) );
+ return misg;
+ }
+
+
+ return Ref<SpatialEditorGizmo>();
+}
+
+
+Ref<FixedMaterial> SpatialEditorGizmos::create_line_material(const Color& p_base_color) {
+
+ Ref<FixedMaterial> line_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ line_material->set_flag(Material::FLAG_UNSHADED, true);
+ line_material->set_line_width(3.0);
+ line_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ line_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, true);
+ line_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,p_base_color);
+
+ return line_material;
+
+}
+
+Ref<FixedMaterial> SpatialEditorGizmos::create_solid_material(const Color& p_base_color) {
+
+ Ref<FixedMaterial> line_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ line_material->set_flag(Material::FLAG_UNSHADED, true);
+ line_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ line_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,p_base_color);
+
+ return line_material;
+
+}
+
+SpatialEditorGizmos::SpatialEditorGizmos() {
+
+ singleton=this;
+
+ handle_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ handle_material->set_flag(Material::FLAG_UNSHADED, true);
+ handle_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(0.8,0.8,0.8));
+
+ handle2_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ handle2_material->set_flag(Material::FLAG_UNSHADED, true);
+ handle2_material->set_fixed_flag(FixedMaterial::FLAG_USE_POINT_SIZE, true);
+ handle_t = SpatialEditor::get_singleton()->get_icon("Editor3DHandle","EditorIcons");
+ handle2_material->set_point_size(handle_t->get_width());
+ handle2_material->set_texture(FixedMaterial::PARAM_DIFFUSE,handle_t);
+ handle2_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1));
+ handle2_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ handle2_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, true);
+
+ light_material = create_line_material(Color(1,1,0.2));
+
+ light_material_omni_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ light_material_omni_icon->set_flag(Material::FLAG_UNSHADED, true);
+ light_material_omni_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
+ light_material_omni_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
+ light_material_omni_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ light_material_omni_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
+ light_material_omni_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoLight","EditorIcons"));
+
+
+ light_material_directional_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ light_material_directional_icon->set_flag(Material::FLAG_UNSHADED, true);
+ light_material_directional_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
+ light_material_directional_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
+ light_material_directional_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ light_material_directional_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
+ light_material_directional_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoDirectionalLight","EditorIcons"));
+
+ camera_material = create_line_material(Color(1.0,0.5,1.0));
+
+
+ navmesh_edge_material = create_line_material(Color(0.1,0.8,1.0));
+ navmesh_solid_material = create_solid_material(Color(0.1,0.8,1.0,0.4));
+ navmesh_edge_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, false);
+ navmesh_solid_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+
+ navmesh_edge_material_disabled = create_line_material(Color(1.0,0.8,0.1));
+ navmesh_solid_material_disabled = create_solid_material(Color(1.0,0.8,0.1,0.4));
+ navmesh_edge_material_disabled->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, false);
+ navmesh_solid_material_disabled->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+
+ skeleton_material = create_line_material(Color(0.6,1.0,0.3));
+ skeleton_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ skeleton_material->set_flag(Material::FLAG_UNSHADED,true);
+ skeleton_material->set_flag(Material::FLAG_ONTOP,true);
+ skeleton_material->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
+
+ //position 3D Shared mesh
+
+ pos3d_mesh = Ref<Mesh>( memnew( Mesh ) );
+ {
+
+ DVector<Vector3> cursor_points;
+ DVector<Color> cursor_colors;
+ float cs = 0.25;
+ cursor_points.push_back(Vector3(+cs,0,0));
+ cursor_points.push_back(Vector3(-cs,0,0));
+ cursor_points.push_back(Vector3(0,+cs,0));
+ cursor_points.push_back(Vector3(0,-cs,0));
+ cursor_points.push_back(Vector3(0,0,+cs));
+ cursor_points.push_back(Vector3(0,0,-cs));
+ cursor_colors.push_back(Color(1,0.5,0.5,0.7));
+ cursor_colors.push_back(Color(1,0.5,0.5,0.7));
+ cursor_colors.push_back(Color(0.5,1,0.5,0.7));
+ cursor_colors.push_back(Color(0.5,1,0.5,0.7));
+ cursor_colors.push_back(Color(0.5,0.5,1,0.7));
+ cursor_colors.push_back(Color(0.5,0.5,1,0.7));
+
+ Ref<FixedMaterial> mat = memnew( FixedMaterial );
+ mat->set_flag(Material::FLAG_UNSHADED,true);
+ mat->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true);
+ mat->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true);
+ mat->set_line_width(3);
+ Array d;
+ d.resize(VS::ARRAY_MAX);
+ d[Mesh::ARRAY_VERTEX]=cursor_points;
+ d[Mesh::ARRAY_COLOR]=cursor_colors;
+ pos3d_mesh->add_surface(Mesh::PRIMITIVE_LINES,d);
+ pos3d_mesh->surface_set_material(0,mat);
+ }
+
+
+ sample_player_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ sample_player_icon->set_flag(Material::FLAG_UNSHADED, true);
+ sample_player_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
+ sample_player_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
+ sample_player_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ sample_player_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
+ sample_player_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoSpatialSamplePlayer","EditorIcons"));
+
+ room_material = create_line_material(Color(1.0,0.6,0.9));
+ portal_material = create_line_material(Color(1.0,0.8,0.6));
+ raycast_material = create_line_material(Color(1.0,0.8,0.6));
+ car_wheel_material = create_line_material(Color(0.6,0.8,1.0));
+ visibility_notifier_material = create_line_material(Color(1.0,0.5,1.0));
+ joint_material = create_line_material(Color(0.6,0.8,1.0));
+
+ stream_player_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ stream_player_icon->set_flag(Material::FLAG_UNSHADED, true);
+ stream_player_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
+ stream_player_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
+ stream_player_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ stream_player_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
+ stream_player_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoSpatialStreamPlayer","EditorIcons"));
+
+ visibility_notifier_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ visibility_notifier_icon->set_flag(Material::FLAG_UNSHADED, true);
+ visibility_notifier_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
+ visibility_notifier_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
+ visibility_notifier_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ visibility_notifier_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
+ visibility_notifier_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("Visible","EditorIcons"));
+
+ {
+
+ DVector<Vector3> vertices;
+
+#undef ADD_VTX
+#define ADD_VTX(m_idx);\
+ vertices.push_back( face_points[m_idx] );
+
+ for (int i=0;i<6;i++) {
+
+
+ Vector3 face_points[4];
+
+ for (int j=0;j<4;j++) {
+
+ float v[3];
+ v[0]=1.0;
+ v[1]=1-2*((j>>1)&1);
+ v[2]=v[1]*(1-2*(j&1));
+
+ for (int k=0;k<3;k++) {
+
+ if (i<3)
+ face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ else
+ face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ }
+ }
+ //tri 1
+ ADD_VTX(0);
+ ADD_VTX(1);
+ ADD_VTX(2);
+ //tri 2
+ ADD_VTX(2);
+ ADD_VTX(3);
+ ADD_VTX(0);
+
+ }
+
+ test_cube_tm = Ref<TriangleMesh>( memnew( TriangleMesh ) );
+ test_cube_tm->create(vertices);
+ }
+
+ shape_material = create_line_material(Color(0.2,1,1.0));
+
+
+}
+
diff --git a/tools/editor/spatial_editor_gizmos.h b/tools/editor/spatial_editor_gizmos.h
index 7f39b648d7..bc7e8ad21d 100644
--- a/tools/editor/spatial_editor_gizmos.h
+++ b/tools/editor/spatial_editor_gizmos.h
@@ -1,487 +1,491 @@
-/*************************************************************************/
-/* spatial_editor_gizmos.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* http://www.godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-#ifndef SPATIAL_EDITOR_GIZMOS_H
-#define SPATIAL_EDITOR_GIZMOS_H
-
-
-#include "tools/editor/plugins/spatial_editor_plugin.h"
-#include "scene/3d/light.h"
-#include "scene/3d/camera.h"
-#include "scene/3d/position_3d.h"
-#include "scene/3d/spatial_sample_player.h"
-#include "scene/3d/spatial_stream_player.h"
-#include "scene/3d/test_cube.h"
-#include "scene/3d/mesh_instance.h"
-#include "scene/3d/body_shape.h"
-#include "scene/3d/room_instance.h"
-#include "scene/3d/visibility_notifier.h"
-#include "scene/3d/portal.h"
-#include "scene/3d/ray_cast.h"
-#include "scene/3d/navigation_mesh.h"
-
-#include "scene/3d/vehicle_body.h"
-#include "scene/3d/collision_polygon.h"
-#include "scene/3d/physics_joint.h"
-
-
-class Camera;
-
-class SpatialGizmoTool : public SpatialEditorGizmo {
-
- OBJ_TYPE(SpatialGizmoTool,SpatialGizmo);
-
- struct Instance{
-
- RID instance;
- Ref<Mesh> mesh;
- RID skeleton;
- bool billboard;
- bool unscaled;
- bool can_intersect;
- bool extra_margin;
- Instance() {
-
- billboard=false;
- unscaled=false;
- can_intersect=false;
- extra_margin=false;
- }
-
- void create_instance(Spatial *p_base);
-
- };
-
- Vector<Vector3> collision_segments;
- Ref<TriangleMesh> collision_mesh;
-
- struct Handle {
- Vector3 pos;
- bool billboard;
- };
-
- Vector<Vector3> handles;
- Vector<Vector3> secondary_handles;
- bool billboard_handle;
-
- bool valid;
- Spatial *base;
- Vector<Instance> instances;
- Spatial *spatial_node;
-protected:
- void add_lines(const Vector<Vector3> &p_lines,const Ref<Material>& p_material,bool p_billboard=false);
- void add_mesh(const Ref<Mesh>& p_mesh,bool p_billboard=false,const RID& p_skeleton=RID());
- void add_collision_segments(const Vector<Vector3> &p_lines);
- void add_collision_triangles(const Ref<TriangleMesh>& p_tmesh);
- void add_unscaled_billboard(const Ref<Material>& p_material,float p_scale=1);
- void add_handles(const Vector<Vector3> &p_handles,bool p_billboard=false,bool p_secondary=false);
-
- void set_spatial_node(Spatial *p_node);
-
-public:
-
- virtual Vector3 get_handle_pos(int p_idx) const;
- virtual bool intersect_frustum(const Camera *p_camera,const Vector<Plane> &p_frustum);
- virtual bool intersect_ray(const Camera *p_camera,const Point2& p_point, Vector3& r_pos, Vector3& r_normal,int *r_gizmo_handle=NULL,bool p_sec_first=false);
-
- void clear();
- void create();
- void transform();
- //void redraw();
- void free();
-
- SpatialGizmoTool();
- ~SpatialGizmoTool();
-};
-
-
-
-class LightSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(LightSpatialGizmo,SpatialGizmoTool);
-
- Light* light;
-
-public:
-
-
- virtual String get_handle_name(int p_idx) const;
- virtual Variant get_handle_value(int p_idx) const;
- virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point);
- virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false);
-
- void redraw();
- LightSpatialGizmo(Light* p_light=NULL);
-
-};
-
-class CameraSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(CameraSpatialGizmo,SpatialGizmoTool);
-
- Camera* camera;
-
-public:
-
-
- virtual String get_handle_name(int p_idx) const;
- virtual Variant get_handle_value(int p_idx) const;
- virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point);
- virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false);
-
- void redraw();
- CameraSpatialGizmo(Camera* p_camera=NULL);
-
-};
-
-
-
-class MeshInstanceSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(MeshInstanceSpatialGizmo,SpatialGizmoTool);
-
- MeshInstance* mesh;
-
-public:
-
- void redraw();
- MeshInstanceSpatialGizmo(MeshInstance* p_mesh=NULL);
-
-};
-
-class Position3DSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(Position3DSpatialGizmo,SpatialGizmoTool);
-
- Position3D* p3d;
-
-public:
-
- void redraw();
- Position3DSpatialGizmo(Position3D* p_p3d=NULL);
-
-};
-
-class SkeletonSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(SkeletonSpatialGizmo,SpatialGizmoTool);
-
- Skeleton* skel;
-
-public:
-
- void redraw();
- SkeletonSpatialGizmo(Skeleton* p_skel=NULL);
-
-};
-
-
-class SpatialPlayerSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(SpatialPlayerSpatialGizmo,SpatialGizmoTool);
-
- SpatialPlayer* splayer;
-
-public:
-
- void redraw();
- SpatialPlayerSpatialGizmo(SpatialPlayer* p_splayer=NULL);
-
-};
-
-class TestCubeSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(TestCubeSpatialGizmo,SpatialGizmoTool);
-
- TestCube* tc;
-
-public:
- void redraw();
- TestCubeSpatialGizmo(TestCube* p_tc=NULL);
-
-};
-
-
-class RoomSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(RoomSpatialGizmo,SpatialGizmoTool);
-
-
- struct _EdgeKey {
-
- Vector3 from;
- Vector3 to;
-
- bool operator<(const _EdgeKey& p_with) const { return from==p_with.from ? to < p_with.to : from < p_with.from; }
- };
-
-
-
- Room* room;
-
-public:
-
- void redraw();
- RoomSpatialGizmo(Room* p_room=NULL);
-
-};
-
-
-class PortalSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(PortalSpatialGizmo,SpatialGizmoTool);
-
- Portal* portal;
-
-public:
-
- void redraw();
- PortalSpatialGizmo(Portal* p_portal=NULL);
-
-};
-
-
-class VisibilityNotifierGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(VisibilityNotifierGizmo ,SpatialGizmoTool);
-
-
- VisibilityNotifier* notifier;
-
-public:
-
- virtual String get_handle_name(int p_idx) const;
- virtual Variant get_handle_value(int p_idx) const;
- virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point);
- virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false);
-
- void redraw();
- VisibilityNotifierGizmo(VisibilityNotifier* p_notifier=NULL);
-
-};
-
-
-
-class CollisionShapeSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(CollisionShapeSpatialGizmo,SpatialGizmoTool);
-
- CollisionShape* cs;
-
-public:
- virtual String get_handle_name(int p_idx) const;
- virtual Variant get_handle_value(int p_idx) const;
- virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point);
- virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false);
- void redraw();
- CollisionShapeSpatialGizmo(CollisionShape* p_cs=NULL);
-
-};
-
-
-class CollisionPolygonSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(CollisionPolygonSpatialGizmo,SpatialGizmoTool);
-
- CollisionPolygon* polygon;
-
-public:
-
- void redraw();
- CollisionPolygonSpatialGizmo(CollisionPolygon* p_polygon=NULL);
-
-};
-
-
-class RayCastSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(RayCastSpatialGizmo,SpatialGizmoTool);
-
- RayCast* raycast;
-
-public:
-
- void redraw();
- RayCastSpatialGizmo(RayCast* p_raycast=NULL);
-
-};
-
-
-
-class VehicleWheelSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(VehicleWheelSpatialGizmo,SpatialGizmoTool);
-
- VehicleWheel* car_wheel;
-
-public:
-
- void redraw();
- VehicleWheelSpatialGizmo(VehicleWheel* p_car_wheel=NULL);
-
-};
-
-
-class NavigationMeshSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(NavigationMeshSpatialGizmo,SpatialGizmoTool);
-
-
- struct _EdgeKey {
-
- Vector3 from;
- Vector3 to;
-
- bool operator<(const _EdgeKey& p_with) const { return from==p_with.from ? to < p_with.to : from < p_with.from; }
- };
-
-
-
- NavigationMeshInstance* navmesh;
-
-public:
-
- void redraw();
- NavigationMeshSpatialGizmo(NavigationMeshInstance* p_navmesh=NULL);
-
-};
-
-
-class PinJointSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(PinJointSpatialGizmo,SpatialGizmoTool);
-
- PinJoint* p3d;
-
-public:
-
- void redraw();
- PinJointSpatialGizmo(PinJoint* p_p3d=NULL);
-
-};
-
-
-class HingeJointSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(HingeJointSpatialGizmo,SpatialGizmoTool);
-
- HingeJoint* p3d;
-
-public:
-
- void redraw();
- HingeJointSpatialGizmo(HingeJoint* p_p3d=NULL);
-
-};
-
-class SliderJointSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(SliderJointSpatialGizmo,SpatialGizmoTool);
-
- SliderJoint* p3d;
-
-public:
-
- void redraw();
- SliderJointSpatialGizmo(SliderJoint* p_p3d=NULL);
-
-};
-
-class ConeTwistJointSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(ConeTwistJointSpatialGizmo,SpatialGizmoTool);
-
- ConeTwistJoint* p3d;
-
-public:
-
- void redraw();
- ConeTwistJointSpatialGizmo(ConeTwistJoint* p_p3d=NULL);
-
-};
-
-
-class Generic6DOFJointSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(Generic6DOFJointSpatialGizmo,SpatialGizmoTool);
-
- Generic6DOFJoint* p3d;
-
-public:
-
- void redraw();
- Generic6DOFJointSpatialGizmo(Generic6DOFJoint* p_p3d=NULL);
-
-};
-
-
-class SpatialEditorGizmos {
-public:
-
- Ref<FixedMaterial> create_line_material(const Color& p_base_color);
- Ref<FixedMaterial> create_solid_material(const Color& p_base_color);
- Ref<FixedMaterial> handle2_material;
- Ref<FixedMaterial> handle_material;
- Ref<FixedMaterial> light_material;
- Ref<FixedMaterial> light_material_omni_icon;
- Ref<FixedMaterial> light_material_directional_icon;
- Ref<FixedMaterial> camera_material;
- Ref<FixedMaterial> skeleton_material;
- Ref<FixedMaterial> room_material;
- Ref<FixedMaterial> portal_material;
- Ref<FixedMaterial> raycast_material;
- Ref<FixedMaterial> visibility_notifier_material;
- Ref<FixedMaterial> car_wheel_material;
- Ref<FixedMaterial> joint_material;
-
- Ref<FixedMaterial> navmesh_edge_material;
- Ref<FixedMaterial> navmesh_solid_material;
- Ref<FixedMaterial> navmesh_edge_material_disabled;
- Ref<FixedMaterial> navmesh_solid_material_disabled;
-
-
- Ref<FixedMaterial> sample_player_icon;
- Ref<FixedMaterial> stream_player_icon;
- Ref<FixedMaterial> visibility_notifier_icon;
-
- Ref<FixedMaterial> shape_material;
- Ref<Texture> handle_t;
-
- Ref<Mesh> pos3d_mesh;
- static SpatialEditorGizmos *singleton;
-
- Ref<TriangleMesh> test_cube_tm;
-
-
- Ref<SpatialEditorGizmo> get_gizmo(Spatial *p_spatial);
-
- SpatialEditorGizmos();
-};
-
-#endif // SPATIAL_EDITOR_GIZMOS_H
-
+/*************************************************************************/
+/* spatial_editor_gizmos.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef SPATIAL_EDITOR_GIZMOS_H
+#define SPATIAL_EDITOR_GIZMOS_H
+
+
+#include "tools/editor/plugins/spatial_editor_plugin.h"
+#include "scene/3d/light.h"
+#include "scene/3d/camera.h"
+#include "scene/3d/position_3d.h"
+#include "scene/3d/spatial_sample_player.h"
+#include "scene/3d/spatial_stream_player.h"
+#include "scene/3d/test_cube.h"
+#include "scene/3d/mesh_instance.h"
+#include "scene/3d/body_shape.h"
+#include "scene/3d/room_instance.h"
+#include "scene/3d/visibility_notifier.h"
+#include "scene/3d/portal.h"
+#include "scene/3d/ray_cast.h"
+#include "scene/3d/navigation_mesh.h"
+
+#include "scene/3d/vehicle_body.h"
+#include "scene/3d/collision_polygon.h"
+#include "scene/3d/physics_joint.h"
+
+
+class Camera;
+
+class SpatialGizmoTool : public SpatialEditorGizmo {
+
+ OBJ_TYPE(SpatialGizmoTool,SpatialGizmo);
+
+ struct Instance{
+
+ RID instance;
+ Ref<Mesh> mesh;
+ RID skeleton;
+ bool billboard;
+ bool unscaled;
+ bool can_intersect;
+ bool extra_margin;
+ Instance() {
+
+ billboard=false;
+ unscaled=false;
+ can_intersect=false;
+ extra_margin=false;
+ }
+
+ void create_instance(Spatial *p_base);
+
+ };
+
+ Vector<Vector3> collision_segments;
+ Ref<TriangleMesh> collision_mesh;
+
+ struct Handle {
+ Vector3 pos;
+ bool billboard;
+ };
+
+ Vector<Vector3> handles;
+ Vector<Vector3> secondary_handles;
+ bool billboard_handle;
+
+ bool valid;
+ Spatial *base;
+ Vector<Instance> instances;
+ Spatial *spatial_node;
+protected:
+ void add_lines(const Vector<Vector3> &p_lines,const Ref<Material>& p_material,bool p_billboard=false);
+ void add_mesh(const Ref<Mesh>& p_mesh,bool p_billboard=false,const RID& p_skeleton=RID());
+ void add_collision_segments(const Vector<Vector3> &p_lines);
+ void add_collision_triangles(const Ref<TriangleMesh>& p_tmesh);
+ void add_unscaled_billboard(const Ref<Material>& p_material,float p_scale=1);
+ void add_handles(const Vector<Vector3> &p_handles,bool p_billboard=false,bool p_secondary=false);
+
+ void set_spatial_node(Spatial *p_node);
+
+public:
+
+ virtual Vector3 get_handle_pos(int p_idx) const;
+ virtual bool intersect_frustum(const Camera *p_camera,const Vector<Plane> &p_frustum);
+ virtual bool intersect_ray(const Camera *p_camera,const Point2& p_point, Vector3& r_pos, Vector3& r_normal,int *r_gizmo_handle=NULL,bool p_sec_first=false);
+
+ void clear();
+ void create();
+ void transform();
+ //void redraw();
+ void free();
+
+ SpatialGizmoTool();
+ ~SpatialGizmoTool();
+};
+
+
+
+class LightSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(LightSpatialGizmo,SpatialGizmoTool);
+
+ Light* light;
+
+public:
+
+
+ virtual String get_handle_name(int p_idx) const;
+ virtual Variant get_handle_value(int p_idx) const;
+ virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point);
+ virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false);
+
+ void redraw();
+ LightSpatialGizmo(Light* p_light=NULL);
+
+};
+
+class CameraSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(CameraSpatialGizmo,SpatialGizmoTool);
+
+ Camera* camera;
+
+public:
+
+
+ virtual String get_handle_name(int p_idx) const;
+ virtual Variant get_handle_value(int p_idx) const;
+ virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point);
+ virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false);
+
+ void redraw();
+ CameraSpatialGizmo(Camera* p_camera=NULL);
+
+};
+
+
+
+class MeshInstanceSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(MeshInstanceSpatialGizmo,SpatialGizmoTool);
+
+ MeshInstance* mesh;
+
+public:
+
+ void redraw();
+ MeshInstanceSpatialGizmo(MeshInstance* p_mesh=NULL);
+
+};
+
+class Position3DSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(Position3DSpatialGizmo,SpatialGizmoTool);
+
+ Position3D* p3d;
+
+public:
+
+ void redraw();
+ Position3DSpatialGizmo(Position3D* p_p3d=NULL);
+
+};
+
+class SkeletonSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(SkeletonSpatialGizmo,SpatialGizmoTool);
+
+ Skeleton* skel;
+
+public:
+
+ void redraw();
+ SkeletonSpatialGizmo(Skeleton* p_skel=NULL);
+
+};
+
+
+
+
+class SpatialPlayerSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(SpatialPlayerSpatialGizmo,SpatialGizmoTool);
+
+ SpatialPlayer* splayer;
+
+public:
+
+ void redraw();
+ SpatialPlayerSpatialGizmo(SpatialPlayer* p_splayer=NULL);
+
+};
+
+
+
+class TestCubeSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(TestCubeSpatialGizmo,SpatialGizmoTool);
+
+ TestCube* tc;
+
+public:
+ void redraw();
+ TestCubeSpatialGizmo(TestCube* p_tc=NULL);
+
+};
+
+
+class RoomSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(RoomSpatialGizmo,SpatialGizmoTool);
+
+
+ struct _EdgeKey {
+
+ Vector3 from;
+ Vector3 to;
+
+ bool operator<(const _EdgeKey& p_with) const { return from==p_with.from ? to < p_with.to : from < p_with.from; }
+ };
+
+
+
+ Room* room;
+
+public:
+
+ void redraw();
+ RoomSpatialGizmo(Room* p_room=NULL);
+
+};
+
+
+class PortalSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(PortalSpatialGizmo,SpatialGizmoTool);
+
+ Portal* portal;
+
+public:
+
+ void redraw();
+ PortalSpatialGizmo(Portal* p_portal=NULL);
+
+};
+
+
+class VisibilityNotifierGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(VisibilityNotifierGizmo ,SpatialGizmoTool);
+
+
+ VisibilityNotifier* notifier;
+
+public:
+
+ virtual String get_handle_name(int p_idx) const;
+ virtual Variant get_handle_value(int p_idx) const;
+ virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point);
+ virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false);
+
+ void redraw();
+ VisibilityNotifierGizmo(VisibilityNotifier* p_notifier=NULL);
+
+};
+
+
+
+class CollisionShapeSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(CollisionShapeSpatialGizmo,SpatialGizmoTool);
+
+ CollisionShape* cs;
+
+public:
+ virtual String get_handle_name(int p_idx) const;
+ virtual Variant get_handle_value(int p_idx) const;
+ virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point);
+ virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false);
+ void redraw();
+ CollisionShapeSpatialGizmo(CollisionShape* p_cs=NULL);
+
+};
+
+
+class CollisionPolygonSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(CollisionPolygonSpatialGizmo,SpatialGizmoTool);
+
+ CollisionPolygon* polygon;
+
+public:
+
+ void redraw();
+ CollisionPolygonSpatialGizmo(CollisionPolygon* p_polygon=NULL);
+
+};
+
+
+class RayCastSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(RayCastSpatialGizmo,SpatialGizmoTool);
+
+ RayCast* raycast;
+
+public:
+
+ void redraw();
+ RayCastSpatialGizmo(RayCast* p_raycast=NULL);
+
+};
+
+
+
+class VehicleWheelSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(VehicleWheelSpatialGizmo,SpatialGizmoTool);
+
+ VehicleWheel* car_wheel;
+
+public:
+
+ void redraw();
+ VehicleWheelSpatialGizmo(VehicleWheel* p_car_wheel=NULL);
+
+};
+
+
+class NavigationMeshSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(NavigationMeshSpatialGizmo,SpatialGizmoTool);
+
+
+ struct _EdgeKey {
+
+ Vector3 from;
+ Vector3 to;
+
+ bool operator<(const _EdgeKey& p_with) const { return from==p_with.from ? to < p_with.to : from < p_with.from; }
+ };
+
+
+
+ NavigationMeshInstance* navmesh;
+
+public:
+
+ void redraw();
+ NavigationMeshSpatialGizmo(NavigationMeshInstance* p_navmesh=NULL);
+
+};
+
+
+class PinJointSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(PinJointSpatialGizmo,SpatialGizmoTool);
+
+ PinJoint* p3d;
+
+public:
+
+ void redraw();
+ PinJointSpatialGizmo(PinJoint* p_p3d=NULL);
+
+};
+
+
+class HingeJointSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(HingeJointSpatialGizmo,SpatialGizmoTool);
+
+ HingeJoint* p3d;
+
+public:
+
+ void redraw();
+ HingeJointSpatialGizmo(HingeJoint* p_p3d=NULL);
+
+};
+
+class SliderJointSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(SliderJointSpatialGizmo,SpatialGizmoTool);
+
+ SliderJoint* p3d;
+
+public:
+
+ void redraw();
+ SliderJointSpatialGizmo(SliderJoint* p_p3d=NULL);
+
+};
+
+class ConeTwistJointSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(ConeTwistJointSpatialGizmo,SpatialGizmoTool);
+
+ ConeTwistJoint* p3d;
+
+public:
+
+ void redraw();
+ ConeTwistJointSpatialGizmo(ConeTwistJoint* p_p3d=NULL);
+
+};
+
+
+class Generic6DOFJointSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(Generic6DOFJointSpatialGizmo,SpatialGizmoTool);
+
+ Generic6DOFJoint* p3d;
+
+public:
+
+ void redraw();
+ Generic6DOFJointSpatialGizmo(Generic6DOFJoint* p_p3d=NULL);
+
+};
+
+
+class SpatialEditorGizmos {
+public:
+
+ Ref<FixedMaterial> create_line_material(const Color& p_base_color);
+ Ref<FixedMaterial> create_solid_material(const Color& p_base_color);
+ Ref<FixedMaterial> handle2_material;
+ Ref<FixedMaterial> handle_material;
+ Ref<FixedMaterial> light_material;
+ Ref<FixedMaterial> light_material_omni_icon;
+ Ref<FixedMaterial> light_material_directional_icon;
+ Ref<FixedMaterial> camera_material;
+ Ref<FixedMaterial> skeleton_material;
+ Ref<FixedMaterial> room_material;
+ Ref<FixedMaterial> portal_material;
+ Ref<FixedMaterial> raycast_material;
+ Ref<FixedMaterial> visibility_notifier_material;
+ Ref<FixedMaterial> car_wheel_material;
+ Ref<FixedMaterial> joint_material;
+
+ Ref<FixedMaterial> navmesh_edge_material;
+ Ref<FixedMaterial> navmesh_solid_material;
+ Ref<FixedMaterial> navmesh_edge_material_disabled;
+ Ref<FixedMaterial> navmesh_solid_material_disabled;
+
+
+ Ref<FixedMaterial> sample_player_icon;
+ Ref<FixedMaterial> stream_player_icon;
+ Ref<FixedMaterial> visibility_notifier_icon;
+
+ Ref<FixedMaterial> shape_material;
+ Ref<Texture> handle_t;
+
+ Ref<Mesh> pos3d_mesh;
+ static SpatialEditorGizmos *singleton;
+
+ Ref<TriangleMesh> test_cube_tm;
+
+
+ Ref<SpatialEditorGizmo> get_gizmo(Spatial *p_spatial);
+
+ SpatialEditorGizmos();
+};
+
+#endif // SPATIAL_EDITOR_GIZMOS_H
+
diff --git a/tools/export/blender25/godot_export_manager.py b/tools/export/blender25/godot_export_manager.py
new file mode 100644
index 0000000000..582d76f94f
--- /dev/null
+++ b/tools/export/blender25/godot_export_manager.py
@@ -0,0 +1,472 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# Script copyright (c) Andreas Esau
+
+bl_info = {
+ "name": "Godot Export Manager",
+ "author": "Andreas Esau",
+ "version": (1, 0),
+ "blender": (2, 7, 0),
+ "location": "Scene Properties > Godot Export Manager",
+ "description": "Godot Export Manager uses the Better Collada Exporter to manage Export Groups and automatically export the objects groups to Collada Files.",
+ "warning": "",
+ "wiki_url": ("http://www.godotengine.org"),
+ "tracker_url": "",
+ "category": "Import-Export"}
+
+import bpy
+from bpy.props import StringProperty, BoolProperty, EnumProperty, FloatProperty, FloatVectorProperty, IntProperty, CollectionProperty, PointerProperty
+import os
+from bpy.app.handlers import persistent
+from mathutils import Vector, Matrix
+
+class godot_export_manager(bpy.types.Panel):
+ bl_label = "Godot Export Manager"
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_context = "scene"
+
+ bpy.types.Scene.godot_export_on_save = BoolProperty(default=False)
+
+ ### draw function for all ui elements
+ def draw(self, context):
+ layout = self.layout
+ split = self.layout.split()
+ scene = bpy.data.scenes[0]
+ ob = context.object
+ scene = context.scene
+
+ row = layout.row()
+ col = row.column()
+ col.prop(scene,"godot_export_on_save",text="Export Groups on save")
+
+ row = layout.row()
+ col = row.column(align=True)
+ op = col.operator("scene.godot_add_objects_to_group",text="Add selected objects to Group",icon="COPYDOWN")
+
+ op = col.operator("scene.godot_delete_objects_from_group",text="Delete selected objects from Group",icon="PASTEDOWN")
+
+
+
+ row = layout.row()
+ col = row.column()
+ col.label(text="Export Groups:")
+
+
+ row = layout.row()
+ col = row.column()
+
+ col.template_list("UI_List_Godot","dummy",scene, "godot_export_groups", scene, "godot_export_groups_index",rows=1,maxrows=10,type='DEFAULT')
+
+ col = row.column(align=True)
+ col.operator("scene.godot_add_export_group",text="",icon="ZOOMIN")
+ col.operator("scene.godot_delete_export_group",text="",icon="ZOOMOUT")
+ col.operator("scene.godot_export_all_groups",text="",icon="EXPORT")
+
+ if len(scene.godot_export_groups) > 0:
+ row = layout.row()
+ col = row.column()
+ group = scene.godot_export_groups[scene.godot_export_groups_index]
+ col.prop(group,"name",text="Group Name")
+ col.prop(group,"export_name",text="Export Name")
+ col.prop(group,"export_path",text="Export Filepath")
+
+ row = layout.row()
+ col = row.column()
+ row = layout.row()
+ col = row.column()
+ col.label(text="Export Settings:")
+
+ col = col.row(align=True)
+ col.prop(group,"apply_loc",toggle=True,icon="MAN_TRANS")
+ col.prop(group,"apply_rot",toggle=True,icon="MAN_ROT")
+ col.prop(group,"apply_scale",toggle=True,icon="MAN_SCALE")
+
+ row = layout.row()
+ col = row.column()
+
+ col.prop(group,"use_include_particle_duplicates")
+ col.prop(group,"use_mesh_modifiers")
+ col.prop(group,"use_tangent_arrays")
+ col.prop(group,"use_triangles")
+ col.prop(group,"use_copy_images")
+ col.prop(group,"use_active_layers")
+ col.prop(group,"use_anim")
+ col.prop(group,"use_anim_action_all")
+ col.prop(group,"use_anim_skip_noexp")
+ col.prop(group,"use_anim_optimize")
+ col.prop(group,"anim_optimize_precision")
+ col.prop(group,"use_metadata")
+
+### Custom template_list look
+class UI_List_Godot(bpy.types.UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+ ob = data
+ slot = item
+ col = layout.row(align=True)
+
+ col.label(text=item.name,icon="GROUP")
+ col.prop(item,"active",text="")
+
+ op = col.operator("scene.godot_select_group_objects",text="",emboss=False,icon="RESTRICT_SELECT_OFF")
+ op.idx = index
+ op = col.operator("scene.godot_export_group",text="",emboss=False,icon="EXPORT")
+ op.idx = index
+
+class add_objects_to_group(bpy.types.Operator):
+ bl_idname = "scene.godot_add_objects_to_group"
+ bl_label = "Add Objects to Group"
+ bl_description = "Adds the selected Objects to the active group below."
+
+ undo = BoolProperty(default=True)
+
+ def execute(self,context):
+ scene = context.scene
+
+ objects_str = ""
+ if len(scene.godot_export_groups) > 0:
+ for i,object in enumerate(context.selected_objects):
+ if object.name not in scene.godot_export_groups[scene.godot_export_groups_index].nodes:
+ node = scene.godot_export_groups[scene.godot_export_groups_index].nodes.add()
+ node.name = object.name
+ if i == 0:
+ objects_str += object.name
+ else:
+ objects_str += ", "+object.name
+
+
+ self.report({'INFO'}, objects_str + " added to group." )
+ if self.undo:
+ bpy.ops.ed.undo_push(message="Objects added to group")
+ else:
+ self.report({'WARNING'}, "Create a group first." )
+ return{'FINISHED'}
+
+class del_objects_from_group(bpy.types.Operator):
+ bl_idname = "scene.godot_delete_objects_from_group"
+ bl_label = "Delete Objects from Group"
+ bl_description = "Delets the selected Objects from the active group below."
+
+ def execute(self,context):
+ scene = context.scene
+
+ if len(scene.godot_export_groups) > 0:
+
+ selected_objects = []
+ for object in context.selected_objects:
+ selected_objects.append(object.name)
+
+ objects_str = ""
+ j = 0
+ for i,node in enumerate(scene.godot_export_groups[scene.godot_export_groups_index].nodes):
+ if node.name in selected_objects:
+ scene.godot_export_groups[scene.godot_export_groups_index].nodes.remove(i)
+
+
+ if j == 0:
+ objects_str += object.name
+ else:
+ objects_str += ", "+object.name
+ j+=1
+
+
+ self.report({'INFO'}, objects_str + " deleted from group." )
+ bpy.ops.ed.undo_push(message="Objects deleted from group")
+ else:
+ self.report({'WARNING'}, "There is no group to delete from." )
+ return{'FINISHED'}
+
+class select_group_objects(bpy.types.Operator):
+ bl_idname = "scene.godot_select_group_objects"
+ bl_label = "Select Group Objects"
+ bl_description = "Will select all group Objects in the scene."
+
+ idx = IntProperty()
+
+ def execute(self,context):
+ scene = context.scene
+ for object in context.scene.objects:
+ object.select = False
+ for node in scene.godot_export_groups[self.idx].nodes:
+ if node.name in bpy.data.objects:
+ bpy.data.objects[node.name].select = True
+ context.scene.objects.active = bpy.data.objects[node.name]
+ return{'FINISHED'}
+
+class export_groups_autosave(bpy.types.Operator):
+ bl_idname = "scene.godot_export_groups_autosave"
+ bl_label = "Export All Groups"
+ bl_description = "Exports all groups to Collada."
+
+ def execute(self,context):
+ scene = context.scene
+ if scene.godot_export_on_save:
+ for i in range(len(scene.godot_export_groups)):
+ if scene.godot_export_groups[i].active:
+ bpy.ops.scene.godot_export_group(idx=i)
+ self.report({'INFO'}, "All Groups exported." )
+ bpy.ops.ed.undo_push(message="Export all Groups")
+ return{'FINISHED'}
+
+class export_all_groups(bpy.types.Operator):
+ bl_idname = "scene.godot_export_all_groups"
+ bl_label = "Export All Groups"
+ bl_description = "Exports all groups to Collada."
+
+ def execute(self,context):
+ scene = context.scene
+
+ for i in range(0,len(scene.godot_export_groups)):
+ bpy.ops.scene.godot_export_group(idx=i,export_all=True)
+
+ self.report({'INFO'}, "All Groups exported." )
+ return{'FINISHED'}
+
+
+class export_group(bpy.types.Operator):
+ bl_idname = "scene.godot_export_group"
+ bl_label = "Export Group"
+ bl_description = "Exports the active group to destination folder as Collada file."
+
+ idx = IntProperty(default=0)
+ export_all = BoolProperty(default=False)
+
+
+ def copy_object_recursive(self,ob,parent,single_user = True):
+ new_ob = bpy.data.objects[ob.name].copy()
+ if single_user or ob.type=="ARMATURE":
+ new_mesh_data = new_ob.data.copy()
+ new_ob.data = new_mesh_data
+ bpy.context.scene.objects.link(new_ob)
+
+ if ob != parent:
+ new_ob.parent = parent
+ else:
+ new_ob.parent = None
+
+ for child in ob.children:
+ self.copy_object_recursive(child,new_ob,single_user)
+ new_ob.select = True
+ return new_ob
+
+ def delete_object(self,ob):
+ if ob != None:
+ for child in ob.children:
+ self.delete_object(child)
+ bpy.context.scene.objects.unlink(ob)
+ bpy.data.objects.remove(ob)
+
+ def convert_group_to_node(self,group):
+ if group.dupli_group != None:
+ for object in group.dupli_group.objects:
+ if object.parent == None:
+ object = self.copy_object_recursive(object,object,True)
+ matrix = Matrix(object.matrix_local)
+ object.matrix_local = Matrix()
+ object.matrix_local *= group.matrix_local
+ object.matrix_local *= matrix
+
+ self.delete_object(group)
+
+ def execute(self,context):
+
+ scene = context.scene
+ group = context.scene.godot_export_groups
+
+ if not group[self.idx].active and self.export_all:
+ return{'FINISHED'}
+
+ for i,object in enumerate(group[self.idx].nodes):
+ if object.name in bpy.data.objects:
+ pass
+ else:
+ group[self.idx].nodes.remove(i)
+ bpy.ops.ed.undo_push(message="Clear not existent Group Nodes.")
+
+ path = group[self.idx].export_path
+ if (path.find("//")==0 or path.find("\\\\")==0):
+ #if relative, convert to absolute
+ path = bpy.path.abspath(path)
+ path = path.replace("\\","/")
+
+ ### if path exists and group export name is set the group will be exported
+ if os.path.exists(path) and group[self.idx].export_name != "":
+
+ context.scene.layers = [True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True]
+
+
+ if group[self.idx].export_name.endswith(".dae"):
+ path = os.path.join(path,group[self.idx].export_name)
+ else:
+ path = os.path.join(path,group[self.idx].export_name+".dae")
+
+ hide_select = []
+ for object in context.scene.objects:
+ hide_select.append(object.hide_select)
+ object.hide_select = False
+ object.select = False
+ context.scene.objects.active = None
+
+ ### make particle duplicates, parent and select them
+ nodes_to_be_added = []
+ if group[self.idx].use_include_particle_duplicates:
+ for i,object in enumerate(group[self.idx].nodes):
+ if bpy.data.objects[object.name].type != "EMPTY":
+ context.scene.objects.active = bpy.data.objects[object.name]
+ bpy.data.objects[object.name].select = True
+ bpy.ops.object.duplicates_make_real()
+ for object in context.selected_objects:
+ nodes_to_be_added.append(object)
+ bpy.ops.object.parent_set(type="OBJECT", keep_transform=False)
+
+ for object in context.selected_objects:
+ object.select = False
+ bpy.data.objects[object.name].select = False
+ context.scene.objects.active = None
+ for object in nodes_to_be_added:
+ object.select = True
+
+ ### select all other nodes from the group
+ for i,object in enumerate(group[self.idx].nodes):
+ if bpy.data.objects[object.name].type == "EMPTY":
+ self.convert_group_to_node(bpy.data.objects[object.name])
+ else:
+ bpy.data.objects[object.name].select = True
+
+ bpy.ops.object.transform_apply(location=group[self.idx].apply_loc, rotation=group[self.idx].apply_rot, scale=group[self.idx].apply_scale)
+ bpy.ops.export_scene.dae(check_existing=True, filepath=path, filter_glob="*.dae", object_types=group[self.idx].object_types, use_export_selected=group[self.idx].use_export_selected, use_mesh_modifiers=group[self.idx].use_mesh_modifiers, use_tangent_arrays=group[self.idx].use_tangent_arrays, use_triangles=group[self.idx].use_triangles, use_copy_images=group[self.idx].use_copy_images, use_active_layers=group[self.idx].use_active_layers, use_anim=group[self.idx].use_anim, use_anim_action_all=group[self.idx].use_anim_action_all, use_anim_skip_noexp=group[self.idx].use_anim_skip_noexp, use_anim_optimize=group[self.idx].use_anim_optimize, anim_optimize_precision=group[self.idx].anim_optimize_precision, use_metadata=group[self.idx].use_metadata)
+
+ self.report({'INFO'}, '"'+group[self.idx].name+'"' + " Group exported." )
+ msg = "Export Group "+group[self.idx].name
+
+ bpy.ops.ed.undo_push(message="")
+ bpy.ops.ed.undo()
+ bpy.ops.ed.undo_push(message=msg)
+
+ else:
+ self.report({'INFO'}, "Define Export Name and Export Path." )
+ return{'FINISHED'}
+
+class add_export_group(bpy.types.Operator):
+ bl_idname = "scene.godot_add_export_group"
+ bl_label = "Adds a new export Group"
+ bl_description = "Creates a new Export Group with the selected Objects assigned to it."
+
+ def execute(self,context):
+ scene = context.scene
+
+ item = scene.godot_export_groups.add()
+ item.name = "New Group"
+ for object in context.selected_objects:
+ node = item.nodes.add()
+ node.name = object.name
+ scene.godot_export_groups_index = len(scene.godot_export_groups)-1
+ bpy.ops.ed.undo_push(message="Create New Export Group")
+ return{'FINISHED'}
+
+class del_export_group(bpy.types.Operator):
+ bl_idname = "scene.godot_delete_export_group"
+ bl_label = "Delets the selected export Group"
+ bl_description = "Delets the active Export Group."
+
+ def invoke(self, context, event):
+ wm = context.window_manager
+ return wm.invoke_confirm(self,event)
+
+ def execute(self,context):
+ scene = context.scene
+
+ scene.godot_export_groups.remove(scene.godot_export_groups_index)
+ if scene.godot_export_groups_index > 0:
+ scene.godot_export_groups_index -= 1
+ bpy.ops.ed.undo_push(message="Delete Export Group")
+ return{'FINISHED'}
+
+class godot_node_list(bpy.types.PropertyGroup):
+ name = StringProperty()
+
+class godot_export_groups(bpy.types.PropertyGroup):
+ name = StringProperty(name="Group Name")
+ export_name = StringProperty(name="scene_name")
+ nodes = CollectionProperty(type=godot_node_list)
+ export_path = StringProperty(subtype="DIR_PATH")
+ active = BoolProperty(default=True,description="Export Group")
+
+ object_types = EnumProperty(name="Object Types",options={'ENUM_FLAG'},items=(('EMPTY', "Empty", ""),('CAMERA', "Camera", ""),('LAMP', "Lamp", ""),('ARMATURE', "Armature", ""),('MESH', "Mesh", ""),('CURVE', "Curve", ""),),default={'EMPTY', 'CAMERA', 'LAMP', 'ARMATURE', 'MESH','CURVE'})
+
+ apply_scale = BoolProperty(name="Apply Scale",description="Apply Scale before export.",default=False)
+ apply_rot = BoolProperty(name="Apply Rotation",description="Apply Rotation before export.",default=False)
+ apply_loc = BoolProperty(name="Apply Location",description="Apply Location before export.",default=False)
+
+ use_export_selected = BoolProperty(name="Selected Objects",description="Export only selected objects (and visible in active layers if that applies).",default=True)
+ use_mesh_modifiers = BoolProperty(name="Apply Modifiers",description="Apply modifiers to mesh objects (on a copy!).",default=True)
+ use_tangent_arrays = BoolProperty(name="Tangent Arrays",description="Export Tangent and Binormal arrays (for normalmapping).",default=False)
+ use_triangles = BoolProperty(name="Triangulate",description="Export Triangles instead of Polygons.",default=False)
+
+ use_copy_images = BoolProperty(name="Copy Images",description="Copy Images (create images/ subfolder)",default=False)
+ use_active_layers = BoolProperty(name="Active Layers",description="Export only objects on the active layers.",default=True)
+ use_anim = BoolProperty(name="Export Animation",description="Export keyframe animation",default=False)
+ use_anim_action_all = BoolProperty(name="All Actions",description=("Export all actions for the first armature found in separate DAE files"),default=False)
+ use_anim_skip_noexp = BoolProperty(name="Skip (-noexp) Actions",description="Skip exporting of actions whose name end in (-noexp). Useful to skip control animations.",default=True)
+ use_anim_optimize = BoolProperty(name="Optimize Keyframes",description="Remove double keyframes",default=True)
+
+ anim_optimize_precision = FloatProperty(name="Precision",description=("Tolerence for comparing double keyframes (higher for greater accuracy)"),min=1, max=16,soft_min=1, soft_max=16,default=6.0)
+
+ use_metadata = BoolProperty(name="Use Metadata",default=True,options={'HIDDEN'})
+ use_include_particle_duplicates = BoolProperty(name="Include Particle Duplicates",default=True)
+
+def register():
+ bpy.utils.register_class(godot_export_manager)
+ bpy.utils.register_class(godot_node_list)
+ bpy.utils.register_class(godot_export_groups)
+ bpy.utils.register_class(add_export_group)
+ bpy.utils.register_class(del_export_group)
+ bpy.utils.register_class(export_all_groups)
+ bpy.utils.register_class(export_groups_autosave)
+ bpy.utils.register_class(export_group)
+ bpy.utils.register_class(add_objects_to_group)
+ bpy.utils.register_class(del_objects_from_group)
+ bpy.utils.register_class(select_group_objects)
+ bpy.utils.register_class(UI_List_Godot)
+
+ bpy.types.Scene.godot_export_groups = CollectionProperty(type=godot_export_groups)
+ bpy.types.Scene.godot_export_groups_index = IntProperty(default=0,min=0)
+
+def unregister():
+ bpy.utils.unregister_class(godot_export_manager)
+ bpy.utils.unregister_class(godot_node_list)
+ bpy.utils.unregister_class(godot_export_groups)
+ bpy.utils.unregister_class(export_groups_autosave)
+ bpy.utils.unregister_class(add_export_group)
+ bpy.utils.unregister_class(del_export_group)
+ bpy.utils.unregister_class(export_all_groups)
+ bpy.utils.unregister_class(export_group)
+ bpy.utils.unregister_class(add_objects_to_group)
+ bpy.utils.unregister_class(del_objects_from_group)
+ bpy.utils.unregister_class(select_group_objects)
+ bpy.utils.unregister_class(UI_List_Godot)
+
+@persistent
+def auto_export(dummy):
+ bpy.ops.scene.godot_export_groups_autosave()
+
+bpy.app.handlers.save_post.append(auto_export)
+
+if __name__ == "__main__":
+ register()
diff --git a/tools/export/blender25/io_scene_dae/__init__.py b/tools/export/blender25/io_scene_dae/__init__.py
index b3e3f70cf0..182ec21e63 100644
--- a/tools/export/blender25/io_scene_dae/__init__.py
+++ b/tools/export/blender25/io_scene_dae/__init__.py
@@ -81,7 +81,7 @@ class ExportDAE(bpy.types.Operator, ExportHelper):
use_mesh_modifiers = BoolProperty(
name="Apply Modifiers",
description="Apply modifiers to mesh objects (on a copy!).",
- default=True,
+ default=False,
)
use_tangent_arrays = BoolProperty(
name="Tangent Arrays",
@@ -104,11 +104,6 @@ class ExportDAE(bpy.types.Operator, ExportHelper):
description="Export only objects on the active layers.",
default=True,
)
- use_exclude_ctrl_bones = BoolProperty(
- name="Exclude Control Bones",
- description="Exclude skeleton bones with names that begin with 'ctrl'.",
- default=True,
- )
use_anim = BoolProperty(
name="Export Animation",
description="Export keyframe animation",
diff --git a/tools/export/blender25/io_scene_dae/export_dae.py b/tools/export/blender25/io_scene_dae/export_dae.py
index 4e1635429b..b846f0e2d8 100644
--- a/tools/export/blender25/io_scene_dae/export_dae.py
+++ b/tools/export/blender25/io_scene_dae/export_dae.py
@@ -87,6 +87,15 @@ def numarr(a,mult=1.0):
s+=" "
return s
+def numarr_alpha(a,mult=1.0):
+ s=" "
+ for x in a:
+ s+=" "+str(x*mult)
+ if len(a) == 3:
+ s+=" 1.0"
+ s+=" "
+ return s
+
def strarr(arr):
s=" "
for x in arr:
@@ -94,8 +103,6 @@ def strarr(arr):
s+=" "
return s
-
-
class DaeExporter:
def validate_id(self,d):
@@ -132,10 +139,10 @@ class DaeExporter:
tup = tup + (self.tangent.x,self.tangent.y,self.tangent.z)
if (self.bitangent!=None):
tup = tup + (self.bitangent.x,self.bitangent.y,self.bitangent.z)
- #for t in self.bones:
- # tup = tup + (t)
- #for t in self.weights:
- # tup = tup + (t)
+ for t in self.bones:
+ tup = tup + (float(t),)
+ for t in self.weights:
+ tup = tup + (float(t),)
return tup
@@ -162,39 +169,66 @@ class DaeExporter:
def export_image(self,image):
-
if (image in self.image_cache):
return self.image_cache[image]
-
+
imgpath = image.filepath
if (imgpath.find("//")==0 or imgpath.find("\\\\")==0):
#if relative, convert to absolute
imgpath = bpy.path.abspath(imgpath)
#path is absolute, now do something!
-
+
if (self.config["use_copy_images"]):
#copy image
basedir = os.path.dirname(self.path)+"/images"
if (not os.path.isdir(basedir)):
os.makedirs(basedir)
- dstfile=basedir+"/"+os.path.basename(imgpath)
- if (not os.path.isfile(dstfile)):
- shutil.copy(imgpath,dstfile)
- imgpath="images/"+os.path.basename(imgpath)
+
+ if os.path.isfile(imgpath):
+ dstfile=basedir+"/"+os.path.basename(imgpath)
+
+ if (not os.path.isfile(dstfile)):
+ shutil.copy(imgpath,dstfile)
+ imgpath="images/"+os.path.basename(imgpath)
+ else:
+ ### if file is not found save it as png file in the destination folder
+ img_tmp_path = image.filepath
+ if img_tmp_path.endswith((".bmp",".rgb",".png",".jpeg",".jpg",".jp2",".tga",".cin",".dpx",".exr",".hdr",".tif")):
+ image.filepath = basedir+"/"+os.path.basename(img_tmp_path)
+ else:
+ image.filepath = basedir+"/"+image.name+".png"
+
+ dstfile=basedir+"/"+os.path.basename(image.filepath)
+
+ if (not os.path.isfile(dstfile)):
+
+ image.save()
+ imgpath="images/"+os.path.basename(image.filepath)
+ image.filepath = img_tmp_path
else:
#export relative, always, no one wants absolute paths.
try:
imgpath = os.path.relpath(imgpath,os.path.dirname(self.path)).replace("\\","/") # export unix compatible always
+
except:
pass #fails sometimes, not sure why
+
+ imgid = self.new_id("image")
+ print("FOR: "+imgpath)
+
+# if (not os.path.isfile(imgpath)):
+# print("NOT FILE?")
+# if imgpath.endswith((".bmp",".rgb",".png",".jpeg",".jpg",".jp2",".tga",".cin",".dpx",".exr",".hdr",".tif")):
+# imgpath="images/"+os.path.basename(imgpath)
+# else:
+# imgpath="images/"+image.name+".png"
- imgid = self.new_id("image")
self.writel(S_IMGS,1,'<image id="'+imgid+'" name="'+image.name+'">')
- self.writel(S_IMGS,2,'<init_from>'+imgpath+'</init_from>"/>')
+ self.writel(S_IMGS,2,'<init_from>'+imgpath+'</init_from>')
self.writel(S_IMGS,1,'</image>')
self.image_cache[image]=imgid
return imgid
@@ -266,25 +300,25 @@ class DaeExporter:
if (emission_tex!=None):
self.writel(S_FX,6,'<texture texture="'+emission_tex+'" texcoord="CHANNEL1"/>')
else:
- self.writel(S_FX,6,'<color>'+numarr(material.diffuse_color,material.emit)+' </color>') # not totally right but good enough
+ self.writel(S_FX,6,'<color>'+numarr_alpha(material.diffuse_color,material.emit)+' </color>') # not totally right but good enough
self.writel(S_FX,5,'</emission>')
self.writel(S_FX,5,'<ambient>')
- self.writel(S_FX,6,'<color>'+numarr(self.scene.world.ambient_color,material.ambient)+' </color>')
+ self.writel(S_FX,6,'<color>'+numarr_alpha(self.scene.world.ambient_color,material.ambient)+' </color>')
self.writel(S_FX,5,'</ambient>')
self.writel(S_FX,5,'<diffuse>')
if (diffuse_tex!=None):
self.writel(S_FX,6,'<texture texture="'+diffuse_tex+'" texcoord="CHANNEL1"/>')
else:
- self.writel(S_FX,6,'<color>'+numarr(material.diffuse_color,material.diffuse_intensity)+'</color>')
+ self.writel(S_FX,6,'<color>'+numarr_alpha(material.diffuse_color,material.diffuse_intensity)+'</color>')
self.writel(S_FX,5,'</diffuse>')
self.writel(S_FX,5,'<specular>')
if (specular_tex!=None):
self.writel(S_FX,6,'<texture texture="'+specular_tex+'" texcoord="CHANNEL1"/>')
else:
- self.writel(S_FX,6,'<color>'+numarr(material.specular_color,material.specular_intensity)+'</color>')
+ self.writel(S_FX,6,'<color>'+numarr_alpha(material.specular_color,material.specular_intensity)+'</color>')
self.writel(S_FX,5,'</specular>')
self.writel(S_FX,5,'<shininess>')
@@ -292,7 +326,7 @@ class DaeExporter:
self.writel(S_FX,5,'</shininess>')
self.writel(S_FX,5,'<reflective>')
- self.writel(S_FX,6,'<color>'+strarr(material.mirror_color)+'</color>')
+ self.writel(S_FX,6,'<color>'+numarr_alpha(material.mirror_color)+'</color>')
self.writel(S_FX,5,'</reflective>')
if (material.use_transparency):
@@ -300,10 +334,11 @@ class DaeExporter:
self.writel(S_FX,6,'<float>'+str(material.alpha)+'</float>')
self.writel(S_FX,5,'</transparency>')
-
+ self.writel(S_FX,5,'<index_of_refraction>')
+ self.writel(S_FX,6,'<float>'+str(material.specular_ior)+'</float>')
+ self.writel(S_FX,5,'</index_of_refraction>')
self.writel(S_FX,4,'</'+shtype+'>')
- self.writel(S_FX,4,'<index_of_refraction>'+str(material.specular_ior)+'</index_of_refraction>')
self.writel(S_FX,4,'<extra>')
self.writel(S_FX,5,'<technique profile="FCOLLADA">')
@@ -457,6 +492,11 @@ class DaeExporter:
apply_modifiers = len(node.modifiers) and self.config["use_mesh_modifiers"]
+ name_to_use = mesh.name
+ #print("name to use: "+mesh.name)
+ if (custom_name!=None and custom_name!=""):
+ name_to_use=custom_name
+
mesh=node.to_mesh(self.scene,apply_modifiers,"RENDER") #is this allright?
triangulate=self.config["use_triangles"]
@@ -488,12 +528,12 @@ class DaeExporter:
mat_assign=[]
uv_layer_count=len(mesh.uv_textures)
- if (len(mesh.uv_textures)):
+ if (has_tangents and len(mesh.uv_textures)):
try:
mesh.calc_tangents()
except:
- print("Warning, blender API is fucked up, not exporting UVs for this object.")
- uv_layer_count=0
+ self.operator.report({'WARNING'},'CalcTangets failed for mesh "'+mesh.name+'", no tangets will be exported.')
+ #uv_layer_count=0
mesh.calc_normals_split()
has_tangents=False
@@ -507,8 +547,8 @@ class DaeExporter:
if (not (f.material_index in surface_indices)):
surface_indices[f.material_index]=[]
- print("Type: "+str(type(f.material_index)))
- print("IDX: "+str(f.material_index)+"/"+str(len(mesh.materials)))
+ #print("Type: "+str(type(f.material_index)))
+ #print("IDX: "+str(f.material_index)+"/"+str(len(mesh.materials)))
try:
#Bizarre blender behavior i don't understand, so catching exception
@@ -567,16 +607,30 @@ class DaeExporter:
if (armature!=None):
wsum=0.0
+ zero_bones=[]
+
for vg in mv.groups:
if vg.group >= len(node.vertex_groups):
continue;
name = node.vertex_groups[vg.group].name
+
if (name in si["bone_index"]):
#could still put the weight as 0.0001 maybe
if (vg.weight>0.001): #blender has a lot of zero weight stuff
v.bones.append(si["bone_index"][name])
v.weights.append(vg.weight)
wsum+=vg.weight
+ if (wsum==0.0):
+ if not self.wrongvtx_report:
+ self.operator.report({'WARNING'},'Mesh for object "'+node.name+'" has unassigned weights. This may look wrong in exported model.')
+ self.wrongvtx_report=True
+
+ #blender can have bones assigned that weight zero so they remain local
+ #this is the best it can be done?
+ v.bones.append(0)
+ v.weights.append(1)
+
+
tup = v.get_tup()
@@ -596,10 +650,7 @@ class DaeExporter:
meshid = self.new_id("mesh")
- if (custom_name!=None):
- self.writel(S_GEOM,1,'<geometry id="'+meshid+'" name="'+custom_name+'">')
- else:
- self.writel(S_GEOM,1,'<geometry id="'+meshid+'" name="'+mesh.name+'">')
+ self.writel(S_GEOM,1,'<geometry id="'+meshid+'" name="'+name_to_use+'">')
self.writel(S_GEOM,2,'<mesh>')
@@ -861,22 +912,35 @@ class DaeExporter:
if (node.data==None):
return
armature=None
+ armcount=0
+ for n in node.modifiers:
+ if (n.type=="ARMATURE"):
+ armcount+=1
if (node.parent!=None):
if (node.parent.type=="ARMATURE"):
armature=node.parent
+ if (armcount>1):
+ self.operator.report({'WARNING'},'Object "'+node.name+'" refers to more than one armature! This is unsopported.')
+ if (armcount==0):
+ self.operator.report({'WARNING'},'Object "'+node.name+'" is child of an armature, but has no armature modifier.')
+
+
+ if (armcount>0 and not armature):
+ self.operator.report({'WARNING'},'Object "'+node.name+'" has armature modifier, but is not a child of an armature. This is unsupported.')
+
if (node.data.shape_keys!=None):
sk = node.data.shape_keys
if (sk.animation_data):
- print("HAS ANIM")
- print("DRIVERS: "+str(len(sk.animation_data.drivers)))
+ #print("HAS ANIM")
+ #print("DRIVERS: "+str(len(sk.animation_data.drivers)))
for d in sk.animation_data.drivers:
if (d.driver):
for v in d.driver.variables:
for t in v.targets:
if (t.id!=None and t.id.name in self.scene.objects):
- print("LINKING "+str(node)+" WITH "+str(t.id.name))
+ #print("LINKING "+str(node)+" WITH "+str(t.id.name))
self.armature_for_morph[node]=self.scene.objects[t.id.name]
@@ -916,6 +980,12 @@ class DaeExporter:
boneidx = si["bone_count"]
si["bone_count"]+=1
bonesid = si["id"]+"-"+str(boneidx)
+ if (bone.name in self.used_bones):
+ if (self.config["use_anim_action_all"]):
+ self.operator.report({'WARNING'},'Bone name "'+bone.name+'" used in more than one skeleton. Actions might export wrong.')
+ else:
+ self.used_bones.append(bone.name)
+
si["bone_index"][bone.name]=boneidx
si["bone_ids"][bone]=boneid
si["bone_names"].append(bonesid)
@@ -978,12 +1048,12 @@ class DaeExporter:
self.writel(S_CAMS,5,'<zfar> '+str(camera.clip_end)+' </zfar>')
self.writel(S_CAMS,4,'</perspective>')
else:
- self.writel(S_CAMS,4,'<orthografic>')
- self.writel(S_CAMS,5,'<xmag> '+str(camera.ortho_scale)+' </xmag>') # I think?
+ self.writel(S_CAMS,4,'<orthographic>')
+ self.writel(S_CAMS,5,'<xmag> '+str(camera.ortho_scale*0.5)+' </xmag>') # I think?
self.writel(S_CAMS,5,'<aspect_ratio> '+str(self.scene.render.resolution_x / self.scene.render.resolution_y)+' </aspect_ratio>')
self.writel(S_CAMS,5,'<znear> '+str(camera.clip_start)+' </znear>')
self.writel(S_CAMS,5,'<zfar> '+str(camera.clip_end)+' </zfar>')
- self.writel(S_CAMS,4,'</orthografic>')
+ self.writel(S_CAMS,4,'</orthographic>')
self.writel(S_CAMS,3,'</technique_common>')
self.writel(S_CAMS,2,'</optics>')
@@ -1176,13 +1246,14 @@ class DaeExporter:
def export_node(self,node,il):
if (not node in self.valid_nodes):
return
+ prev_node = bpy.context.scene.objects.active
bpy.context.scene.objects.active = node
self.writel(S_NODES,il,'<node id="'+self.validate_id(node.name)+'" name="'+node.name+'" type="NODE">')
il+=1
self.writel(S_NODES,il,'<matrix sid="transform">'+strmtx(node.matrix_local)+'</matrix>')
- print("NODE TYPE: "+node.type+" NAME: "+node.name)
+ #print("NODE TYPE: "+node.type+" NAME: "+node.name)
if (node.type=="MESH"):
self.export_mesh_node(node,il)
elif (node.type=="CURVE"):
@@ -1199,13 +1270,14 @@ class DaeExporter:
il-=1
self.writel(S_NODES,il,'</node>')
+ bpy.context.scene.objects.active = prev_node #make previous node active again
def is_node_valid(self,node):
if (not node.type in self.config["object_types"]):
return False
if (self.config["use_active_layers"]):
valid=False
- print("NAME: "+node.name)
+ #print("NAME: "+node.name)
for i in range(20):
if (node.layers[i] and self.scene.layers[i]):
valid=True
@@ -1355,7 +1427,7 @@ class DaeExporter:
# Change frames first, export objects last
# This improves performance enormously
- print("anim from: "+str(start)+" to "+str(end)+" allowed: "+str(allowed))
+ #print("anim from: "+str(start)+" to "+str(end)+" allowed: "+str(allowed))
for t in range(start,end+1):
self.scene.frame_set(t)
key = t * frame_len - frame_sub
@@ -1409,7 +1481,7 @@ class DaeExporter:
bone_name=self.skeleton_info[node]["bone_ids"][bone]
if (not (bone_name in xform_cache)):
- print("has bone: "+bone_name)
+ #print("has bone: "+bone_name)
xform_cache[bone_name]=[]
posebone = node.pose.bones[bone.name]
@@ -1441,12 +1513,13 @@ class DaeExporter:
return tcn
def export_animations(self):
- tmp_mat = [] # workaround by ndee
- for s in self.skeletons: # workaround by ndee
- tmp_bone_mat = [] # workaround by ndee
- for bone in s.pose.bones: # workaround by ndee
- tmp_bone_mat.append(Matrix(bone.matrix_basis)) # workaround by ndee
- tmp_mat.append([Matrix(s.matrix_local),tmp_bone_mat]) # workaround by ndee -> stores skeleton and bone transformations
+ tmp_mat = []
+ for s in self.skeletons:
+ tmp_bone_mat = []
+ for bone in s.pose.bones:
+ tmp_bone_mat.append(Matrix(bone.matrix_basis))
+ bone.matrix_basis = Matrix()
+ tmp_mat.append([Matrix(s.matrix_local),tmp_bone_mat])
self.writel(S_ANIM,0,'<library_animations>')
@@ -1481,7 +1554,7 @@ class DaeExporter:
bones.append(dp)
allowed_skeletons=[]
- for i,y in enumerate(self.skeletons): # workaround by ndee
+ for i,y in enumerate(self.skeletons):
if (y.animation_data):
for z in y.pose.bones:
if (z.bone.name in bones):
@@ -1489,37 +1562,42 @@ class DaeExporter:
allowed_skeletons.append(y)
y.animation_data.action=x;
- y.matrix_local = tmp_mat[i][0] # workaround by ndee -> resets the skeleton transformation.
- for j,bone in enumerate(s.pose.bones): # workaround by ndee
- bone.matrix_basis = Matrix() # workaround by ndee -> resets the bone transformations. Important if bones in follwing actions miss keyframes
+ y.matrix_local = tmp_mat[i][0]
+ for j,bone in enumerate(s.pose.bones):
+ bone.matrix_basis = Matrix()
- print("allowed skeletons "+str(allowed_skeletons))
+ #print("allowed skeletons "+str(allowed_skeletons))
- print(str(x))
+ #print(str(x))
tcn = self.export_animation(int(x.frame_range[0]),int(x.frame_range[1]+0.5),allowed_skeletons)
framelen=(1.0/self.scene.render.fps)
start = x.frame_range[0]*framelen
end = x.frame_range[1]*framelen
- print("Export anim: "+x.name)
+ #print("Export anim: "+x.name)
self.writel(S_ANIM_CLIPS,1,'<animation_clip name="'+x.name+'" start="'+str(start)+'" end="'+str(end)+'">')
for z in tcn:
self.writel(S_ANIM_CLIPS,2,'<instance_animation url="#'+z+'"/>')
self.writel(S_ANIM_CLIPS,1,'</animation_clip>')
+ if (len(tcn)==0):
+ self.operator.report({'WARNING'},'Animation clip "'+x.name+'" contains no tracks.')
+
self.writel(S_ANIM_CLIPS,0,'</library_animation_clips>')
- for i,s in enumerate(self.skeletons): # workaround by ndee
+
+ for i,s in enumerate(self.skeletons):
if (s.animation_data==None):
continue
if s in cached_actions:
s.animation_data.action = bpy.data.actions[cached_actions[s]]
else:
s.animation_data.action = None
- for j,bone in enumerate(s.pose.bones): # workaround by ndee
- bone.matrix_basis = tmp_mat[i][1][j] # workaround by ndee -> resets the bone transformation to what they were before exporting.
+ for j,bone in enumerate(s.pose.bones):
+ bone.matrix_basis = tmp_mat[i][1][j]
+
else:
self.export_animation(self.scene.frame_start,self.scene.frame_end)
@@ -1590,7 +1668,8 @@ class DaeExporter:
f.write(bytes('</COLLADA>\n',"UTF-8"))
return True
- def __init__(self,path,kwargs):
+ def __init__(self,path,kwargs,operator):
+ self.operator=operator
self.scene=bpy.context.scene
self.last_id=0
self.scene_name=self.new_id("scene")
@@ -1604,6 +1683,10 @@ class DaeExporter:
self.config=kwargs
self.valid_nodes=[]
self.armature_for_morph={}
+ self.used_bones=[]
+ self.wrongvtx_report=False
+
+
@@ -1615,9 +1698,11 @@ def save(operator, context,
**kwargs
):
- exp = DaeExporter(filepath,kwargs)
+ exp = DaeExporter(filepath,kwargs,operator)
exp.export()
+
+
return {'FINISHED'} # so the script wont run after we have batch exported.
diff --git a/tools/freetype/SCsub b/tools/freetype/SCsub
index a31b8c4602..65b4827f9c 100644
--- a/tools/freetype/SCsub
+++ b/tools/freetype/SCsub
@@ -65,7 +65,5 @@ if (env["freetype"]=="builtin"):
# lib = env.Library("freetype_builtin",ft_sources)
# env.Prepend(LIBS=[lib])
-
-Export('env')
-
+Export('env')
diff --git a/tools/html_fs/godot.html b/tools/html_fs/godot.html
new file mode 100644
index 0000000000..36761deb90
--- /dev/null
+++ b/tools/html_fs/godot.html
@@ -0,0 +1,1317 @@
+<!doctype html>
+<html lang="en-us">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>Emscripten-Generated Code</title>
+ <style>
+ body {
+ font-family: arial;
+ margin: 0;
+ padding: none;
+ }
+
+ .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
+ div.emscripten { text-align: center; }
+ div.emscripten_border { border: 1px solid black; }
+ /* the canvas *must not* have any border or padding, or mouse coords will be wrong */
+ canvas.emscripten { border: 0px none; }
+
+ #emscripten_logo {
+ display: inline-block;
+ margin: 0;
+ }
+
+ .spinner {
+ height: 30px;
+ width: 30px;
+ margin: 0;
+ margin-top: 20px;
+ margin-left: 20px;
+ display: inline-block;
+ vertical-align: top;
+
+ -webkit-animation: rotation .8s linear infinite;
+ -moz-animation: rotation .8s linear infinite;
+ -o-animation: rotation .8s linear infinite;
+ animation: rotation 0.8s linear infinite;
+
+ border-left: 5px solid rgb(235, 235, 235);
+ border-right: 5px solid rgb(235, 235, 235);
+ border-bottom: 5px solid rgb(235, 235, 235);
+ border-top: 5px solid rgb(120, 120, 120);
+
+ border-radius: 100%;
+ background-color: rgb(189, 215, 46);
+ }
+
+ @-webkit-keyframes rotation {
+ from {-webkit-transform: rotate(0deg);}
+ to {-webkit-transform: rotate(360deg);}
+ }
+ @-moz-keyframes rotation {
+ from {-moz-transform: rotate(0deg);}
+ to {-moz-transform: rotate(360deg);}
+ }
+ @-o-keyframes rotation {
+ from {-o-transform: rotate(0deg);}
+ to {-o-transform: rotate(360deg);}
+ }
+ @keyframes rotation {
+ from {transform: rotate(0deg);}
+ to {transform: rotate(360deg);}
+ }
+
+ #status {
+ display: inline-block;
+ vertical-align: top;
+ margin-top: 30px;
+ margin-left: 20px;
+ font-weight: bold;
+ color: rgb(120, 120, 120);
+ }
+
+ #progress {
+ height: 20px;
+ width: 30px;
+ }
+
+ #controls {
+ display: inline-block;
+ float: right;
+ vertical-align: top;
+ margin-top: 30px;
+ margin-right: 20px;
+ }
+
+ #output {
+ width: 100%;
+ height: 200px;
+ margin: 0 auto;
+ margin-top: 10px;
+ display: block;
+ background-color: black;
+ color: white;
+ font-family: 'Lucida Console', Monaco, monospace;
+ outline: none;
+ }
+ </style>
+ </head>
+ <body>
+ <a href="http://emscripten.org">
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?><svg
+ version="1.1"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ width="296px"
+ height="78px"
+ viewBox="420 120 100 170"
+ enable-background="new 0 0 900 400"
+ xml:space="preserve"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="emscripten_powered_by_logo.svg"><metadata
+ id="metadata345"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs343"><linearGradient
+ y2="247.6265"
+ x2="225.1929"
+ y1="152.499"
+ x1="225.1929"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5104"><stop
+ id="stop5106"
+ style="stop-color:#C1D72F"
+ offset="0.3227531" /><stop
+ id="stop5108"
+ style="stop-color:#BCD631"
+ offset="0.45119295" /><stop
+ id="stop5110"
+ style="stop-color:#AFD136"
+ offset="0.64491969" /><stop
+ id="stop5112"
+ style="stop-color:#ABD037"
+ offset="1" /><a:midPointStop
+ style="stop-color:#C1D72F"
+ offset="0.0123" /><a:midPointStop
+ style="stop-color:#C1D72F"
+ offset="0.3086" /><a:midPointStop
+ style="stop-color:#ABD037"
+ offset="1" /></linearGradient><linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_2_"
+ id="linearGradient5120"
+ x1="397.56918"
+ y1="128.12726"
+ x2="397.56918"
+ y2="166.25996"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.103059,0,0,1.103059,-38.997823,3.1312145)" /><filter
+ inkscape:collect="always"
+ id="filter5126"><feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.56377237"
+ id="feGaussianBlur5128" /></filter><linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_2_"
+ id="linearGradient5134"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.103059,0,0,1.103059,-38.997823,3.1312145)"
+ x1="397.56918"
+ y1="128.12726"
+ x2="397.56918"
+ y2="166.25996" /></defs><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1440"
+ inkscape:window-height="838"
+ id="namedview341"
+ showgrid="false"
+ inkscape:zoom="0.63555556"
+ inkscape:cx="224.82424"
+ inkscape:cy="-52.085109"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="Layer_1" /><g
+ id="g5130"
+ transform="matrix(0.91591318,0,0,0.91591318,28.176953,14.143571)"><path
+ transform="matrix(1.103059,0,0,1.103059,-35.073492,-16.03923)"
+ id="path5122"
+ style="fill:#383838;fill-opacity:0.34705882;stroke:none;filter:url(#filter5126)"
+ d="m 494.39333,173.6323 c 0.57407,0.28703 1.87073,1.00226 2.89426,1.02855 0.55732,0.0143 1.14006,-0.1672 1.60262,-0.4784 1.20466,-0.81046 2.23561,-2.03031 2.72683,-3.39661 0.19424,-0.54027 0.0238,-1.72222 0.0238,-1.72222 l -3.82713,-14.06478 -1.98533,0 0.50231,-2.67891 6.36261,0 2.55939,12.22285 4.78392,-9.68746 -2.00924,0 0,-2.65498 7.19979,0 -11.00301,22.38875 -1.69829,1.91358 -2.29628,1.3395 -2.46371,0.26312 -2.29628,-0.21528 -2.79859,-1.36342 z m -12.0637,-14.56445 c -0.93698,1.88565 -1.70261,4.35262 -0.81842,6.26333 0.36549,0.78976 1.35098,1.19428 2.192,1.41737 0.60934,0.16133 1.29167,0.0999 1.88775,-0.10468 0.48126,-0.1655 0.8829,-0.5224 1.255,-0.8697 0.40341,-0.3768 0.77723,-0.80461 1.03505,-1.29262 0.21864,-0.41395 0.40236,-0.84786 0.49325,-1.30698 0.20667,-1.0485 0.35879,-2.1079 0.33583,-3.17631 -0.0184,-0.87403 -0.0789,-1.87107 -0.47711,-2.64959 -0.26344,-0.51379 -0.77017,-0.71849 -1.33113,-0.85633 -0.42395,-0.10479 -0.81432,-0.0626 -1.21773,0.10517 -0.65479,0.27273 -1.2544,0.5311 -1.82112,0.95764 -0.57331,0.4317 -1.21403,0.86959 -1.53337,1.5127 z m 0.65588,-4.31208 c 0,0 2.19341,-1.80738 3.45549,-2.27082 0.71718,-0.26365 3.45363,-0.65258 4.15,-0.3378 1.47292,0.66633 2.26103,1.57529 2.7222,2.60001 0.46118,1.02472 0.69944,2.59956 0.79701,3.73627 0.13278,1.55027 -0.13682,3.77629 -0.53404,5.74843 -0.30079,1.49256 -1.01883,2.74423 -1.83478,3.92156 -1.06526,1.5373 -1.82382,2.15116 -3.66756,2.46594 -0.98864,0.16889 -1.93845,0.46787 -3.25466,0.0928 -1.4384,-0.40963 -2.35273,-0.81244 -3.39599,-1.63337 -0.72524,-0.57054 -1.16043,-1.54043 -1.16043,-1.54043 l 0,2.82636 -4.8903,0 3.39872,-23.01602 -1.92242,-0.85888 0.0403,-2.38127 7.25847,0.0534 z m -23.77803,2.20447 c 0.29175,1.49273 0.0813,4.83252 -0.86111,6.69751 -0.3062,0.60617 -0.94813,1.32967 -1.55479,1.6983 -1.01515,0.61713 -2.21688,1.21322 -3.3966,1.07639 -0.47944,-0.0541 -0.97036,-0.34348 -1.24383,-0.74151 -0.47686,-0.69328 -0.43621,-1.55032 -0.45448,-2.39198 -0.024,-1.06873 0.13137,-2.23775 0.38272,-3.277 0.18705,-0.7744 0.4229,-1.58254 0.86111,-2.24844 0.39037,-0.59323 0.92628,-1.12617 1.55478,-1.45909 0.54854,-0.29014 1.19695,-0.38467 1.81791,-0.40664 0.63637,-0.0231 1.3031,0.0385 1.88966,0.28704 0.3875,0.16453 0.92361,0.3524 1.00463,0.76542 z m 1.29312,-9.69052 -0.64254,6.12262 c 0,0 -1.68393,-0.96858 -2.605,-1.25148 -0.73032,-0.22434 -1.50312,-0.36654 -2.26624,-0.33838 -0.97069,0.0345 -1.91182,0.22099 -2.81751,0.57088 -0.9185,0.35497 -1.78344,0.94565 -2.49338,1.62792 -0.88025,0.84538 -1.51404,1.90455 -2.02977,3.0106 -0.39653,0.84993 -0.69517,1.75284 -0.87975,2.67232 -0.22875,1.14241 -0.44415,2.38719 -0.43937,3.55197 0.01,1.44865 0.0623,2.89489 0.54092,4.26214 0.25525,0.72907 0.71643,1.40578 1.28572,1.9283 0.56835,0.52207 1.29566,0.87604 2.02935,1.11621 0.41072,0.13491 0.85346,0.17274 1.28579,0.16935 1.00285,-0.01 2.03715,-0.0883 2.97671,-0.43999 0.66497,-0.2489 1.21759,-0.73399 1.79298,-1.1502 0.75304,-0.54475 2.16476,-1.86006 2.16476,-1.86006 l 0,1.62374 -0.5751,0 0,1.48807 6.86709,0 0,-2.84135 -1.92841,0 3.21374,-23.57782 -7.37422,0 0,2.33412 z m -93.60062,7.55781 2.33363,15.57933 6.23084,0 4.04243,-11.34169 1.62654,11.34169 5.88425,0 7.05633,-16.38872 0,-2.0141 -6.1713,0 0,2.82349 1.88966,0 -4.04243,10.16973 -0.74151,0 -1.29167,-12.55773 -5.38194,0 -4.7361,12.50989 -1.55478,-12.94538 -6.86496,0 0,2.82349 z m -12.15,0.72146 c -0.56264,0.0892 -1.03524,0.17358 -1.53086,0.45447 -0.737,0.41808 -1.46132,0.95771 -1.91357,1.67437 -0.44123,0.70048 -0.53204,1.57581 -0.66975,2.39196 -0.1751,1.04003 -0.20064,2.10306 -0.19136,3.15741 0.01,0.81614 -0.0138,1.66577 0.35879,2.39197 0.1904,0.37315 0.52874,0.80945 0.88503,1.02855 0.56015,0.34453 1.06632,0.55494 1.72222,0.598 0.72597,0.0483 1.48801,-0.18852 2.10493,-0.57408 0.59422,-0.37072 1.03334,-0.97401 1.38735,-1.5787 0.46117,-0.78744 0.70905,-1.69257 0.90895,-2.58334 0.20377,-0.90704 0.33579,-1.84565 0.28703,-2.77468 -0.0491,-0.92714 -0.18211,-1.88434 -0.57407,-2.72684 -0.2728,-0.58681 -0.70954,-1.00753 -1.29166,-1.29165 -0.44403,-0.21628 -0.99455,-0.24402 -1.48303,-0.16744 z m -6.62442,-0.73581 c 0.65404,-0.6664 1.4072,-1.25479 2.23273,-1.69161 1.0305,-0.54505 2.16429,-0.92749 3.31518,-1.11604 1.51307,-0.24806 3.09342,-0.2847 4.60036,0 0.88055,0.16632 1.78322,0.44742 2.50307,0.98113 0.77409,0.57312 1.35279,1.40936 1.79291,2.26639 0.42901,0.83457 0.6828,1.77223 0.77798,2.70605 0.16564,1.61985 0.024,3.29135 -0.37201,4.87103 -0.33328,1.33759 -0.88436,2.64754 -1.65745,3.78889 -0.67549,0.99679 -1.52894,1.91262 -2.53721,2.5709 -0.89957,0.58746 -1.9718,0.87641 -3.01035,1.15006 -0.87153,0.22963 -1.77166,0.4095 -2.67235,0.40576 -1.21068,-0.01 -2.47998,-0.0817 -3.58589,-0.57511 -1.09854,-0.48896 -1.89728,-1.32739 -2.60455,-2.30013 -0.61123,-0.83995 -1.02561,-1.59975 -1.31932,-2.87516 -0.2125,-0.9233 -0.40006,-2.19912 -0.37215,-3.14592 0.0335,-1.16537 0.3568,-2.74121 0.83416,-3.80434 0.52547,-1.17098 1.17609,-2.3161 2.07489,-3.2319 z m 94.95184,13.82318 c -2.20516,1.01761 -4.61429,1.69636 -7.02343,1.69636 -5.32726,0 -7.22678,-3.12145 -7.22678,-7.22678 0,-7.1251 4.54685,-11.19645 10.0772,-11.19645 3.7324,0 5.56453,1.69625 5.56453,4.47856 0,4.85189 -5.12329,6.27735 -10.41633,6.82001 0.10168,1.73076 0.81446,3.32485 3.3592,3.32485 1.2218,0 2.88401,-0.37315 4.91982,-1.22099 z m -3.22292,-11.77374 c 0,-0.81423 -0.57695,-1.28891 -1.62876,-1.28891 -1.89988,0 -3.46041,1.66212 -3.96978,4.34287 1.45897,-0.20368 5.59854,-0.91613 5.59854,-3.05396 z m -30.33408,11.77374 c -2.2054,1.01761 -4.61457,1.69636 -7.02371,1.69636 -5.32653,0 -7.22671,-3.12145 -7.22671,-7.22678 0,-7.1251 4.54679,-11.19645 10.07785,-11.19645 3.73175,0 5.56382,1.69625 5.56382,4.47856 0,4.85189 -5.12273,6.27735 -10.41568,6.82001 0.10142,1.73076 0.81422,3.32485 3.35884,3.32485 1.22158,0 2.8842,-0.37315 4.91994,-1.22099 z m -3.22305,-11.77374 c 0,-0.81423 -0.57638,-1.28891 -1.62883,-1.28891 -1.89959,0 -3.46023,1.66212 -3.96971,4.34287 1.4591,-0.20368 5.59854,-0.91613 5.59854,-3.05396 z m -82.36051,20.5268 -0.0679,-0.13571 0.98406,-5.66614 2.10303,-15.16698 c 0.0687,-0.40664 -0.0332,-0.61046 -0.30522,-0.71214 l -1.66259,-0.61111 0.37379,-2.57855 6.78556,0 -0.40663,2.71427 0.10142,0.0335 c 2.0016,-1.86631 4.10566,-3.08743 6.24306,-3.08743 2.91821,0 4.95366,1.86577 4.95366,6.78561 0,4.68241 -1.83206,11.6379 -8.14271,11.6379 -2.20534,0 -3.42694,-0.84825 -4.68256,-1.73039 l -0.74621,5.08917 c -0.0341,0.37361 0.0326,0.50898 0.47457,0.54273 l 3.42697,0.33969 -0.37385,2.5447 -9.0589,0 z m 6.78613,-12.04485 c 0.84787,0.71258 1.96788,1.32305 3.22348,1.32305 2.74798,0 3.76601,-3.86811 3.76601,-6.85368 0,-2.002 -0.47476,-3.32542 -1.76432,-3.32542 -1.35696,0 -3.08763,1.4591 -4.30913,2.54506 z m 81.08934,4.85147 0.33969,-2.54464 1.56064,-0.2038 c 0.47498,-0.0683 0.5429,-0.1695 0.61084,-0.67837 l 1.42466,-10.34864 c 0.0335,-0.37315 -0.0335,-0.61046 -0.33914,-0.71214 l -1.69691,-0.61111 0.37365,-2.57855 6.71797,0 -0.44097,3.05395 0.10191,0.0679 c 1.32326,-1.89982 3.22359,-3.46042 5.39485,-3.46042 0.7463,0 2.0359,0.13582 2.61295,0.30538 l -0.84863,6.17508 -3.96972,-0.13582 -0.10157,-1.76443 c -0.0335,-0.30537 -0.10223,-0.40701 -0.37391,-0.40701 -0.64452,0 -1.69636,0.78027 -2.64651,1.76455 l -1.18674,8.61817 c -0.0687,0.54303 -0.0334,0.64474 0.47477,0.67874 l 3.22351,0.27142 -0.37384,2.51081 -10.8575,0 z"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cssscccccccccccccccccssssssssccssscssssscsssccccccccsssssssssccsccsssssssssscsscccccccccccccccccccccccccccccccsssscsssssscscsssssssscsssssssssscsssscsccsscscsssscsccsscsccccccccccsssccccccccssscccccccccccccsccccsccccccc" /><path
+ sodipodi:nodetypes="cssscccccccccccccccccssssssssccssscssssscsssccccccccsssssssssccsccsssssssssscsscccccccccccccccccccccccccccccccsssscsssssscscsssssssscsssssssssscsssscsccsscscsssscsccsscsccccccccccsssccccccccssscccccccccccccsccccsccccccc"
+ inkscape:connector-curvature="0"
+ d="m 509.55935,174.26011 c 0.63327,0.31663 2.06355,1.10555 3.19256,1.13455 0.61476,0.0158 1.25757,-0.18443 1.76781,-0.5277 1.3288,-0.89397 2.46618,-2.23946 3.00784,-3.74661 0.21419,-0.59598 0.0258,-1.89972 0.0258,-1.89972 l -4.22153,-15.51428 -2.18993,0 0.55406,-2.95501 7.01835,0 2.82313,13.48255 5.27696,-10.68586 -2.21631,0 0,-2.92858 7.94179,0 -12.13698,24.69605 -1.87332,2.11078 -2.5329,1.4776 -2.71762,0.29022 -2.53295,-0.23748 -3.08699,-1.50392 z m -13.30698,-16.06545 c -1.0335,2.08005 -1.87803,4.80122 -0.90274,6.90883 0.4032,0.87116 1.49018,1.31738 2.4179,1.56347 0.67214,0.17793 1.42477,0.1102 2.08233,-0.11548 0.53084,-0.1826 0.97383,-0.5762 1.38432,-0.9593 0.44502,-0.4157 0.85733,-0.8875 1.14176,-1.42582 0.24113,-0.45665 0.44375,-0.93526 0.54404,-1.44168 0.22797,-1.1566 0.3958,-2.3252 0.37043,-3.50371 -0.0204,-0.96413 -0.0869,-2.06387 -0.52631,-2.92259 -0.29054,-0.56679 -0.84946,-0.79259 -1.46826,-0.94463 -0.46761,-0.11559 -0.89829,-0.0686 -1.34322,0.11597 -0.72226,0.30083 -1.38368,0.5859 -2.00879,1.05634 -0.63242,0.4762 -1.33915,0.9593 -1.69146,1.6686 z m 0.72346,-4.75648 c 0,0 2.41951,-1.99358 3.81169,-2.50482 0.79109,-0.29085 3.80953,-0.71977 4.57766,-0.3726 1.6247,0.73503 2.49408,1.73759 3.00274,2.86791 0.50868,1.13043 0.77154,2.86756 0.87911,4.12137 0.14648,1.71007 -0.15092,4.16549 -0.58904,6.34083 -0.33179,1.64636 -1.12383,3.02703 -2.02388,4.32576 -1.17506,1.6957 -2.01178,2.37286 -4.04556,2.72004 -1.09051,0.18629 -2.13814,0.51607 -3.59006,0.10268 -1.5866,-0.45183 -2.59522,-0.89615 -3.74599,-1.8017 -0.79994,-0.62933 -1.28003,-1.6992 -1.28003,-1.6992 l 0,3.11766 -5.39426,0 3.74898,-25.38802 -2.12052,-0.94738 0.0443,-2.62669 8.00657,0.0587 z m -26.22853,2.43167 c 0.32185,1.64663 0.0893,5.33062 -0.9498,7.38781 -0.33781,0.66857 -1.04588,1.46667 -1.7151,1.8733 -1.11975,0.68073 -2.44527,1.33822 -3.7466,1.18729 -0.52883,-0.0601 -1.07036,-0.37888 -1.37203,-0.81791 -0.52601,-0.76478 -0.48121,-1.71012 -0.50128,-2.63848 -0.0263,-1.17893 0.14487,-2.46835 0.42212,-3.6147 0.20635,-0.8543 0.4665,-1.74564 0.94981,-2.48024 0.43067,-0.65433 1.02178,-1.24217 1.71508,-1.60939 0.60504,-0.32004 1.32025,-0.42437 2.00521,-0.44854 0.70197,-0.0251 1.4374,0.0425 2.08446,0.31654 0.4274,0.18153 1.01882,0.3888 1.10813,0.84432 z m 1.42642,-10.68922 -0.70874,6.75362 c 0,0 -1.85753,-1.06838 -2.8735,-1.38048 -0.80562,-0.24744 -1.65802,-0.40424 -2.49984,-0.37318 -1.07069,0.0382 -2.10882,0.24369 -3.1078,0.62968 -1.01321,0.39157 -1.96724,1.04315 -2.75039,1.79572 -0.97095,0.93248 -1.67003,2.10085 -2.23897,3.3208 -0.43738,0.93753 -0.76677,1.93354 -0.9704,2.94777 -0.2523,1.26016 -0.4899,2.63324 -0.48461,3.91802 0.011,1.59795 0.0683,3.19329 0.59661,4.70144 0.28155,0.80417 0.79028,1.55058 1.41822,2.127 0.62695,0.57587 1.4292,0.96634 2.23856,1.23121 0.45301,0.14881 0.94135,0.19054 1.41828,0.18685 1.10615,-0.011 2.24705,-0.0973 3.28346,-0.48539 0.73352,-0.2745 1.34304,-0.80959 1.97773,-1.2687 0.83064,-0.60085 2.38786,-2.05176 2.38786,-2.05176 l 0,1.79104 -0.63429,0 0,1.64147 7.57478,0 0,-3.13415 -2.12721,0 3.54494,-26.00772 -8.13411,0 0,2.57462 z m -103.24702,8.33671 2.57413,17.18493 6.87304,0 4.45903,-12.51049 1.79414,12.51049 6.49065,0 7.78353,-18.07772 0,-2.2217 -6.8073,0 0,3.11449 2.08446,0 -4.45903,11.21783 -0.8179,0 -1.42488,-13.85193 -5.93654,0 -5.2242,13.79919 -1.71497,-14.27958 -7.57246,0 0,3.11449 z m -13.4021,0.79586 c -0.62064,0.0982 -1.14194,0.19148 -1.68866,0.50127 -0.813,0.46118 -1.61192,1.05641 -2.11077,1.84697 -0.48673,0.77268 -0.58683,1.73821 -0.73875,2.63846 -0.1932,1.14723 -0.22134,2.31976 -0.21116,3.48281 0.011,0.90024 -0.0148,1.83747 0.39579,2.63847 0.21,0.41165 0.58324,0.89285 0.97623,1.13455 0.61796,0.38003 1.17622,0.61214 1.89972,0.6596 0.80077,0.0533 1.64141,-0.20792 2.32189,-0.63318 0.65546,-0.40892 1.13978,-1.07441 1.53029,-1.7414 0.50878,-0.86864 0.78215,-1.86707 1.00265,-2.84964 0.22477,-1.00044 0.37039,-2.03585 0.31663,-3.06058 -0.0541,-1.02274 -0.20091,-2.07854 -0.63327,-3.00784 -0.3009,-0.64731 -0.78264,-1.11143 -1.42476,-1.42485 -0.48983,-0.23858 -1.09705,-0.26912 -1.63583,-0.18464 z m -7.30711,-0.81171 c 0.72143,-0.735 1.55219,-1.38409 2.46282,-1.86591 1.1367,-0.60125 2.38729,-1.02309 3.65678,-1.23104 1.66908,-0.27366 3.41222,-0.314 5.07446,0 0.97135,0.18342 1.96702,0.49352 2.76107,1.08223 0.85389,0.63222 1.49219,1.55466 1.97771,2.49999 0.47321,0.92057 0.7531,1.95483 0.85808,2.98495 0.18274,1.78675 0.0263,3.63055 -0.41031,5.37303 -0.36757,1.47539 -0.97545,2.92034 -1.82825,4.17929 -0.74509,1.09959 -1.68654,2.10982 -2.79871,2.8359 -0.99227,0.64796 -2.175,0.96671 -3.32055,1.26856 -0.96139,0.25333 -1.95426,0.4517 -2.94774,0.44756 -1.33549,-0.011 -2.73559,-0.0897 -3.9555,-0.63431 -1.21174,-0.53936 -2.09278,-1.46419 -2.87295,-2.53723 -0.67423,-0.92645 -1.13131,-1.76457 -1.45532,-3.17146 -0.2344,-1.0184 -0.44126,-2.42572 -0.41044,-3.47012 0.0365,-1.28547 0.39349,-3.02371 0.92005,-4.19644 0.57967,-1.29168 1.29729,-2.5548 2.2888,-3.565 z m 104.73744,15.24778 c -2.43247,1.12251 -5.0899,1.87126 -7.74734,1.87126 -5.87626,0 -7.97147,-3.44315 -7.97147,-7.97158 0,-7.8594 5.0154,-12.35035 11.11569,-12.35035 4.11711,0 6.13803,1.87105 6.13803,4.94016 0,5.35189 -5.65129,6.92425 -11.48983,7.52281 0.11219,1.90916 0.89836,3.66755 3.7054,3.66755 1.3477,0 3.18121,-0.41165 5.42682,-1.34689 z m -3.55513,-12.98704 c 0,-0.89823 -0.63635,-1.42181 -1.79655,-1.42181 -2.09568,0 -3.81712,1.83342 -4.37899,4.79047 1.60937,-0.22468 6.17554,-1.01053 6.17554,-3.36866 z m -33.46028,12.98704 c -2.4327,1.12251 -5.09006,1.87126 -7.74751,1.87126 -5.87553,0 -7.97151,-3.44315 -7.97151,-7.97158 0,-7.8594 5.01539,-12.35035 11.11645,-12.35035 4.11635,0 6.13722,1.87105 6.13722,4.94016 0,5.35189 -5.65062,6.92425 -11.48908,7.52281 0.11182,1.90916 0.89812,3.66755 3.70494,3.66755 1.34748,0 3.1815,-0.41165 5.42704,-1.34689 z m -3.55514,-12.98704 c 0,-0.89823 -0.63578,-1.42181 -1.79674,-1.42181 -2.09539,0 -3.81683,1.83342 -4.37881,4.79047 1.60951,-0.22468 6.17555,-1.01053 6.17555,-3.36866 z m -90.84852,22.6422 -0.0749,-0.14971 1.08546,-6.25004 2.31984,-16.73008 c 0.0757,-0.44854 -0.0367,-0.67336 -0.33673,-0.78554 l -1.83388,-0.67411 0.41228,-2.84425 7.48486,0 -0.44853,2.99397 0.11182,0.0371 c 2.2079,-2.05871 4.52887,-3.40563 6.88646,-3.40563 3.21901,0 5.46427,2.05807 5.46427,7.48491 0,5.16501 -2.02094,12.8373 -8.98192,12.8373 -2.43264,0 -3.78014,-0.93565 -5.16516,-1.90869 l -0.82311,5.61357 c -0.0376,0.41212 0.0356,0.56148 0.52347,0.59873 l 3.78017,0.37469 -0.41234,2.8069 -9.9925,0 z m 7.48553,-13.28615 c 0.93528,0.78598 2.17068,1.45946 3.55568,1.45946 3.03118,0 4.15411,-4.26682 4.15411,-7.56009 0,-2.2083 -0.52366,-3.66812 -1.94612,-3.66812 -1.49686,0 -3.40583,1.6095 -4.75323,2.80736 z m 89.44624,5.35147 0.37469,-2.80694 1.72154,-0.2248 c 0.52388,-0.0753 0.5988,-0.1869 0.67374,-0.74827 l 1.57152,-11.41514 c 0.0365,-0.41155 -0.0368,-0.67336 -0.3741,-0.78554 l -1.87181,-0.67411 0.41215,-2.84425 7.41037,0 -0.48647,3.36865 0.11241,0.0749 c 1.45966,-2.09562 3.55581,-3.81702 5.95085,-3.81702 0.8232,0 2.2457,0.14982 2.88225,0.33688 l -0.93613,6.81148 -4.37882,-0.14982 -0.11196,-1.94633 c -0.0371,-0.33677 -0.11284,-0.44891 -0.41252,-0.44891 -0.71092,0 -1.87116,0.86067 -2.91921,1.94635 l -1.30904,9.50637 c -0.0757,0.59903 -0.0368,0.71124 0.52367,0.74874 l 3.55571,0.29932 -0.41234,2.76961 -11.9765,0 z"
+ style="fill:url(#linearGradient5134);fill-opacity:1;stroke:none"
+ id="path5080" /></g><path
+ fill="#E2E2E2"
+ d="M256.023,135.437H196.36c-16.432,0-29.8,13.368-29.8,29.8v73.527c0,16.432,13.368,29.8,29.8,29.8h59.663 c16.433,0,29.801-13.368,29.801-29.8v-73.527C285.824,148.805,272.456,135.437,256.023,135.437z M191.561,165.236 c0-2.646,2.153-4.8,4.8-4.8h59.663c2.647,0,4.801,2.153,4.801,4.8v73.527c0,2.646-2.153,4.8-4.801,4.8H196.36 c-2.646,0-4.8-2.153-4.8-4.8V165.236z"
+ id="path3" /><path
+ d="m 531.664,250.155 h 18.498 l -2.809,18.064 h 5.59 37.586 l 2.6,-17.718 c 4.98,-1.091 9.133,-3.455 12.512,-6.693 3.084,4.075 8.566,7.37 18.252,7.37 6.338,0 12.775,-1.807 17.174,-3.687 4.254,2.399 9.463,3.687 15.459,3.687 3.088,0 6.236,-0.355 9.426,-1.023 h 67.135 l 3.354,-24.827 -5.445,-0.764 1.879,-13.356 c 0.371,-2.386 0.449,-4.66 0.449,-6.156 l -0.008,-0.375 c -0.457,-12.191 -8.139,-19.765 -20.045,-19.765 -2.404,0 -4.623,0.314 -6.676,0.852 h -34.189 l -0.035,0.244 c -2.527,-0.701 -5.41,-1.096 -8.686,-1.096 -3.801,0 -7.406,0.555 -10.76,1.598 l 0.105,-0.746 h -12.467 l 1.826,-12.951 H 615.08 l -1.846,7.658 c -1.373,5.704 -2.213,5.793 -4.453,6.03 l -4.508,0.477 c -3.049,-1.424 -6.357,-2.065 -9.602,-2.065 -2.135,0 -4.275,0.284 -6.416,0.852 h -19.291 c 0.502,-1.772 0.775,-3.674 0.775,-5.678 0,-9.601 -6.846,-16.305 -16.646,-16.305 -11.055,0 -18.775,7.721 -18.775,18.776 0,0.951 0.082,1.869 0.219,2.764 -2.135,-0.288 -4.277,-0.409 -5.553,-0.409 -2.053,0 -4.072,0.288 -6.045,0.852 h -31.342 c -2.74,-0.553 -5.641,-0.852 -8.537,-0.852 -7.138,0 -13.492,1.674 -18.808,4.723 l -3.451,-1.461 c -3.711,-1.571 -11.232,-3.262 -18.979,-3.262 -8.933,0 -16.383,2.56 -21.576,7.016 -3.265,-4.473 -8.523,-7.016 -15.228,-7.016 -4.822,0 -9.021,1.477 -12.572,3.44 -2.996,-2.204 -6.796,-3.44 -11.115,-3.44 -2.327,0 -4.48,0.315 -6.476,0.852 h -33.963 l -0.035,0.245 c -2.526,-0.702 -5.41,-1.097 -8.687,-1.097 -20.458,0 -35.307,16.031 -35.307,38.117 0,17.363 10.785,28.149 28.148,28.149 3.087,0 6.236,-0.356 9.426,-1.023 h 88.816 c 3.706,0.676 7.669,1.023 11.154,1.023 8.907,0 16.278,-2.375 21.51,-6.593 4.872,4.252 11.585,6.593 19.728,6.593 3.053,0 6.206,-0.368 9.286,-1.023 h 44.664 2.069 z"
+ id="path5"
+ inkscape:connector-curvature="0"
+ style="fill:#e2e2e2" /><path
+ fill="#F5F5F5"
+ d="M255.023,133.437H195.36c-16.432,0-29.8,13.368-29.8,29.8v73.527c0,16.432,13.368,29.8,29.8,29.8h59.663 c16.433,0,29.801-13.368,29.801-29.8v-73.527C284.824,146.805,271.456,133.437,255.023,133.437z M190.561,163.236 c0-2.646,2.153-4.8,4.8-4.8h59.663c2.647,0,4.801,2.153,4.801,4.8v73.527c0,2.646-2.153,4.8-4.801,4.8H195.36 c-2.646,0-4.8-2.153-4.8-4.8V163.236z"
+ id="path7" /><g
+ id="g9"><g
+ id="g11"><path
+ fill="#FBFDF8"
+ d="M195.361,251.626c-8.161,0-14.8-6.64-14.8-14.8v-73.527c0-8.161,6.639-14.8,14.8-14.8h59.663 c8.161,0,14.8,6.639,14.8,14.8v73.527c0,8.16-6.639,14.8-14.8,14.8H195.361z"
+ id="path13" /><path
+ fill="#F0F4E1"
+ d="M255.024,152.499c5.964,0,10.8,4.835,10.8,10.8v73.527c0,5.965-4.835,10.8-10.8,10.8h-59.663 c-5.964,0-10.8-4.835-10.8-10.8v-73.527c0-5.964,4.835-10.8,10.8-10.8H255.024 M255.024,144.499h-59.663 c-10.366,0-18.8,8.434-18.8,18.8v73.527c0,10.366,8.434,18.8,18.8,18.8h59.663c10.366,0,18.8-8.434,18.8-18.8v-73.527 C273.824,152.933,265.391,144.499,255.024,144.499L255.024,144.499z"
+ id="path15" /></g><defs
+ id="defs17"><filter
+ id="Adobe_OpacityMaskFilter"
+ filterUnits="userSpaceOnUse"
+ x="176.562"
+ y="144.499"
+ width="97.263"
+ height="111.127"><feColorMatrix
+ type="matrix"
+ values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
+ color-interpolation-filters="sRGB"
+ result="source"
+ id="feColorMatrix20" /></filter></defs><mask
+ maskUnits="userSpaceOnUse"
+ x="176.562"
+ y="144.499"
+ width="97.263"
+ height="111.127"
+ id="SVGID_1_"><g
+ filter="url(#Adobe_OpacityMaskFilter)"
+ id="g23"><image
+ overflow="visible"
+ width="422"
+ height="480"
+ xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAgEBLAEsAAD/7AARRHVja3kAAQAEAAAAHgAA/+4AIUFkb2JlAGTAAAAAAQMA EAMCAwYAAAg2AAAQ4QAAF1b/2wCEABALCwsMCxAMDBAXDw0PFxsUEBAUGx8XFxcXFx8eFxoaGhoX Hh4jJSclIx4vLzMzLy9AQEBAQEBAQEBAQEBAQEABEQ8PERMRFRISFRQRFBEUGhQWFhQaJhoaHBoa JjAjHh4eHiMwKy4nJycuKzU1MDA1NUBAP0BAQEBAQEBAQEBAQP/CABEIAeMBqQMBIgACEQEDEQH/ xACjAAEAAgMBAQAAAAAAAAAAAAAABQYBAwQHAgEBAQAAAAAAAAAAAAAAAAAAAAEQAAEDAQQKAwAC AwEAAAAAAAABAwQCMRMUBRBQEjMVJQYWNgcgESEwI5AiMkARAAEBAwsEAQIFAwUBAAAAAAABMQID EFAycqOz0wQ0RaURIXGRIEFRMGEiExRAgRKh0SMzQxUSAQAAAAAAAAAAAAAAAAAAAJD/2gAMAwEA AhEDEQAAANUJsrZYFfFgV8WBXxYEL0ki5fo6GjJuaRuaRuaRuaRuaRuaRuaRuaRuaRuaRuaRuaRu aRuaMHQ5dR3ojnJ9XxYFfFgV8WD0jxf2AodbslbAD6mDhlpLvI/qkuiovZL7CGzNfRCJwQacEGnB Bp0QSdEEnRBJ0QSdEEnRBJ3BBpwQacEHidwQXzPfBA6bBqK5w2nlKVH3iJitt+gAeweP+wFDrdkr Y+vmaN02k6+e3d2Gjo6N0c2zoyaM7xozuGluGluGluGluGluGluGluGluGluGluGluGnG8c/z1YO PVIfJF80xoIGPsfBVVrl6hIrD7+B7B4/7AUOt2StnXaYyxHTJ6ZKvrqb4x9MgAAAAAAAAAAAAAAA DGR8692Dh4pbkIKJscTVNiLdVY1+weP+wFDgJ+JLJORs3XbIc3dGz6ZAAAAAAAAAAAAAAAAAAAPn R0ayMi5uLqv1S51eIT2Dx/2AofB38Ra5uIm6kOzm6o+gAAAAAAAAAAAAAAAAAAAPj7+TkjJWNIOt 2et1WfYPH/YIofH2cZcJyEnKkenn6IyAAAAAAAAAAAAAAAAAAABjODmjZONIWt2WtVWPYPH/AGCK Hx9nIXGcg5ypLfo3xkAAAAAAAAAAAAAAAAAAADGcHPGyUaQ1astaqseweP8AsEUPk6+QuM7BTtSW 7TujIAAAAAAAAAAAAAAAAAAAGM4OeOkY4hqzZqzVY9g8f9gih8nXyFxnYKdqS3ad0ZAAAAAAAAAA AAAAAAAAAAxnBzx0jHENWbNWarHsHj/sEUPk6+QuM7BTtSW7TujIAAAAAAAAAAAAAAAAAAAGM4Oe OkY4hqzZqzVY9g8f9gih8nXyFxnYKdqS3ad0ZAAAAAAAAAAAAAAAAAAAAxnBzx0jHENWbNWarHsH j/sEUPk6+QuM7BTtSW7TujIAAAAAAAAAAAAAAAAAAAGM4OeOkY4hqzZqzVY9g8f9gih8nXyFxnYK dqS3ad0ZAAAAAAAAAAAAAAAAAAAAxnBzx0jHENWbNWarHsHj/sEUPk6+QuM7BTtSW7TujIAAAAAA AAAAAAAAAAAAAGM4OeOkY4hqzZqzVY9g8f8AYIofJ18hcZ2Cnakt2ndGQAAAAAAAAAAAAAAAAAAA MZwc8dIxxDVmzVmqx7B4/wCwRQ+Tr5C4zsFO1JbtO6MgAAAAAAAAAAAAAAAAAAAYzg546RjiGrNm rNVj2Dx/2CKHydfIXGdgp2pLdp3RkAAAAAAAAAAAAAAAAAAADGcHPHSMcQ1Zs1ZqseweP+wRQ+Tr 5C4zsFO1JbtO6MgAAAAAAAAAAAAAAAAAAAYzg546RjiGrNmrNVj2Dx/2CKHydfIXGdgp2pLdp3Rk AAAAAAAAAAAAAAAAAAADGcHPHSMcQ1Zs1ZqseweP+wRQ+Tr5C4zsFO1JbtO6MgAAAAAAAAAAAAAA AAAAAYzg546RjiGrNmrNVj2Dx/2CKHydfIXGdgp2pLdp3RkAAAAAAAAAAAAAAAAAAADGcHPHSMcQ 1Zs1ZqseweP+wRQ+Tr4y5TkHOVJb9G+MgAAAAAAAAAAAAAAAAAAAYzg542SjSGrVlrVVj2Dx/wBg ih8fZxlxnIKcqT6ObpjIAAAAAAAAAAAAAAAAAAAGM4OeNkY0h61Za1VY9g8f9gih8Xbwlxm4GbqW 6uLrj7AAAAAAAAAAAAAAAAAAAA+fr5OaNkI0ia1Y61Vb9g8f9gihxknCl1m65N1OdsZ3x0ZxkAAA AAAAAAAAAAAAAAAAx8fek5ozui6jazYKsRPsHj/sEUOu2Ktlqn6XZ6scjBSRLbOPpjYxkAAAAAAA AAAAAAAAAAYfJjm+uM0xXVE1xVOZr0Y9g8f9gKHW7JWz7s1W6i9SdYlasXXB9pLbI7fHY5/s3NeT 7fGT6fI+nyPp8j6fI+nyPp8j6fI+nyPp8j6fI+nyPp8D7x8fJtxp1m7Tp5jbw/MfWIjbXTk5SHsH j/sBQ63ZK2AdthqO8vXbUZWrJ0V/oJ7ZB7Sa+ofJMIkS6IySyJRLIkSyJEsiRLIkSyJEsiRLIkSy JVLYiRLYicEr8xfwSemN0kjy8PIdkfxQp0xWEAPYPH/YCh1uyVsAAz08ome2si37qZkumaULspIu 2aRkuyki7KSLspIuyki7KSLspIuyki7KSLtilC6qSLtilC6fNNFu5qz8k7wcI+vkAAHsHj/sBWoQ AAAAAAAAAAAAAAAAAAAAAAAAAHpAf//aAAgBAgABBQD/ACi//9oACAEDAAEFAP8AKL//2gAIAQEA AQUA6w6rz/LM+776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvv qs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qz vvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++ qzvvqs776rMfLPYHlHyRFUbivuDeUv1FOSVKJkRwE4CcAOAHADgBwA4AcAOAHADgBwA4AcAOAHAD gBwA4AcAOAHADgBwA4AcAOAnARciFyRUK8ndQcgyGxaaqf4fYHlHwRFUjZe68RsqbpGoKIUQkKYY kISEYJDAmBMCYEwJgTAmBMCYEwJgTAmBMCYEwJgTAmBMCYEwJgTAmCQWELCKoSFcNByEhIyxusk5 VVQV0VUL8vYHlGltupyqDlaIMREQaijcUoilMUSKgkZDDIYZDDIYZDDIYZDDIYZDDIYZDDIYZDDI YZDDIYZDDIYZDDIYZDDIYZDDIYZDDIYZDDILGQWKVRSuKORR2KPRCZltDiSYrjFXx9geUaG26nKs vy9KEjxhmONRxuOUMFLAjIjJdF0XRdIXSF0hdF0XRdF0XRdF0XRdF0XRdF0XRdIXSF0hdIXRdCsi sisFTBXHHY49GH4xMhU10y4tTFfw9geUCJ9rlcL6SNHGGBlgbZKGilsShDZQ+kPr/wBX0fSGygtC CtoVNDjI6wPsElgzCGjlLrdTden2B5QZfGvnYbCIkdkYZGmihsSn61ItJXQOtD7JIZJTBm0X6+Hs DyhP1cpjbLcVojtDLY3QIn1qZU+yugebJDRKaJ7CVUvtq27o9geURaLx6C19JFbI7Y1QU0/WqFQd oH6CS2TG/wAzZrZd0ewPKMqo2n4VH5FoGKBunVTifj9JJpJdBnVH+mj2B5RkqfdcOki0jFJQn5qm pPx5CTSS6TOKf6tHsDyjI0/2hIRU/GUKbNU1DyfklCWhm6f06PYHlGRf9QkIqfjKCWapWx4kkszj daPYHlGQ2wrItjImqlseJJMM43Wj2B5RkNsEjWNarUeJJMM43Oj2B5RkNsEjWNarUeJJMM43Oj2B 5RkNsGyLY1qtR6ySTDONzo9geUZDbBsi2NarUesk2TDON1o9geUZDbBsjWNarUesk2TDON1o9geU ZDbBsjWNarUeskkwzjdaPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDb BsjWNarUeskkwzjdaPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDbBsj WNarUeskkwzjdaPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDbBsjWNa rUeskkwzjdaPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDbBsjWNarUe skkwzjdaPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDbBsjWNarUeskk wzjdaPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDbBsjWNarUeskkwzj daPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDbBsjWNarUeskkwzjdaP YHlGQ2wbI1jWq1HrJNkwzjdaPYHlGQ2wbItjWq1HrJNkwzjdaPYHlGQ2wbItjWq1HrJJMM43Oj2B 5RkNsGyNY1qtR4kkwzjc6PYHlGQ2wSNY1qtR4kkwzjc6PYHlGQ2wrItjImqlseJJMM43Wj2B5RkV sJSKv4yolmqVseJJLM43Wj2B5Rkf/UJSKv4ypTZqmoeX8kqS1M43Oj2B5Rki/wC0Koi1DKlC/mqa h5SSpLUzdf6tHsDyjJ6/p2HURaxiobX81TWv4/USaiXUZy59N6PYHlGXubEiE5+RaxisaqEXVCjl Q/WSayXX+Zy59ro9geUUVbNeXPpVRFdI7gzWUVfeqK6h2skOElwmu/ST3bx/R7A8oMpk/SxHiM8M OjThTX9iLqWqr6HHB50kOkp4zSVsUVKqro9geUDLit15fLSumM+MPjTw26UuCVH2moPsWoqcK3R1 4feJD5MkIiTpKvO6fYHlGiFLViuHLSpGJAzIGnyh8peKXRHEEcQ20NtDbQ2kNpDaQ2kNpDaQ2kNp DaQ2kNpDaQ2kNpDaQ2kNpDaQ2kNpDaQ20NtDbQVxBXEFdKnit8cfHpA/IJMn6TMp+0vw9geUaYU+ pmqJNprRmUNSRuSUSSmQgkgSQI+X5fl+X5fl+X5fl+X5fl+X5fl+X5fl+X5fl+X5fl+X4r4sgWQV SCuSOSR2SPSiRLREzDMlUVVVfh7A8o+EeW4wsTNKKxmYijcsollMspliSxJZjDGIYxDGIYxDGIYx DGIYxDGIYxDGIYxDGIYxDGIYxDGIYxDGIYxDGIYxDGIYxBZYssqllUsrljksdmISsxooSVmLjyqq r8vYHlHxRVRWZ77QznNI3mzSlGZUKU5hSJmKHEUOIocRQ4jScRQ4ihxFDiKHEUOIocRQ4ihxFDiK HEUOIocRQ4ihxFDiKHEUOIocRpOIocRQ4ihxFBcxQXMEKsxpHM1aQezmhB/M3nCquqtfn7A8o/hS utC9dL50vnS/eL94v3i/eL94v3i/eL94v3i/eL94v3i/eL94v3i/eL94v3i/eL94v3i/eL94v3i/ eL50vnS9dLytT7X+PrDhvHuTHJjkxyY5McmOTHJjkxyY5McmOTHJjkxyY5McmOTHJjkxyY5McmOT HJjkxyY5McmOTHJjkxyY5McmOTHJjkxyY5McmOTHJjkxyY5McmOTHJjkxyY/pP/aAAgBAgIGPwBR f//aAAgBAwIGPwBRf//aAAgBAQEGPwCPk8jmv2su47DV1z9uE90V5xHl7vuKrTXWMHDNdYwcM11j BwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHD NdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11 jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMH DNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDP+z/AEd/2MzUhXbvz7H6XVO/ Y7vFJSkpSUpKUlKSlJSkpSUpKUlKSlJSkpSUpKUlKSlJSkpSUpKUlKSlJSkpSUpKUlKSlJSkp+le p3d6ndOn4OZqQrt349EOqp0QT9PVfzGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGC9X RVh9/wAjo8nRfnmakK7d+H+LqdVEefTqonYYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBg wYMGDBeqd/uKip2+/wAszUhXbsqOutURVT9SidhgwYMlZ/XsGDBgvYVFQVOnb6fHM1IV27J0QR95 O6idhOwyaWC9hU6d/oK6v0+GZqQrt2RFVOyCdhBJrUU/cRO6N+GZqQrt06CL07qIJNiijydGjzsu ZqQrt0dd/MRBBJsUUU/y+8uZqQrt06/YQQSbFFFOv2WXM1IV26KIJNyij0uZqQrt0e8iCTcoo/Lm akK7dHvIggk2KKKPy5mpCu3R7yIJNyij8uZqQrt0e8iCTcoo/LmakK7dHvIgk3KKPy5mpCu3R7yI JNyij8uZqQrt0e8iCTaooo/LmakK7dHvIgk3KKPy5mpCu3R7yIJNyij8uZqQrt0e8iCCTaoo/Lma kK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu3R7y IIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCCTaoo /LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu 3R7yIIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCC Taoo/LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCTcoo/LmakK7dHvIgk2qKKPy5m pCu3R7yIJNyij8uZqQrt0e8iCTcoo/LmakK7dHvIgk3KKPy5mpCu3R7yIJNyij8uZqQrt0e8iCCT aoo/LmakK7dHvIgk3KKPy5mpCu3R4QSblFHpczUhXboqfcQQSbVFFT7y5mpCu3RPzEEEmxRRRHZc zUhXbojyfRR1RBJsUUUX7JLmakK7dk/bVfAgk2KKL37qwVV+suZqQrt2RHk+giook2L3F7i9GJ8M zUhXbsqItFRFRRO40aNmVo0aL3FhuL5+OZqQrt34I69REVFGjRo0aNGjf6po0aNGjRo0XuK5DXv9 zqrV+OZqQrt349UXt9hEVeijRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0Xq9/YVH V6OnVflmakK7d+XVOw3qh0e7FM7PJ7KQ0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRpSKaH6V6nR 3sh1eXr+BmakK7d/C7KqFJfZTX2U19lNfZTX2U19lN72U3vZTe9lN72U3vZTe9lN72U3vZTe9lN7 2U3vZTe9lN72U3vZTe9lN72U3vZTe9lN72U19lNfZTX2U19lNfZSX2d3l/Ej/wAj/wCf+7/jD6/y f5/7tBOnX+L/AMfr+5tPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPK m08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptP Km08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKn/jan//Z"
+ transform="matrix(0.24 0 0 0.24 174.5615 142.499)"
+ id="image25"></image></g></mask><g
+ opacity="0.09"
+ mask="url(#SVGID_1_)"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ id="g27"><path
+ fill="#1D2915"
+ a:adobe-blending-mode="normal"
+ a:adobe-opacity-share="0"
+ d="M195.361,251.626 c-8.161,0-14.8-6.64-14.8-14.8v-73.527c0-8.161,6.639-14.8,14.8-14.8h59.663c8.161,0,14.8,6.639,14.8,14.8v73.527 c0,8.16-6.639,14.8-14.8,14.8H195.361z"
+ id="path29" /><path
+ fill="#1D2915"
+ a:adobe-blending-mode="normal"
+ a:adobe-opacity-share="0"
+ d="M255.024,152.499 c5.964,0,10.8,4.835,10.8,10.8v73.527c0,5.965-4.835,10.8-10.8,10.8h-59.663c-5.964,0-10.8-4.835-10.8-10.8v-73.527 c0-5.964,4.835-10.8,10.8-10.8H255.024 M255.024,144.499h-59.663c-10.366,0-18.8,8.434-18.8,18.8v73.527 c0,10.366,8.434,18.8,18.8,18.8h59.663c10.366,0,18.8-8.434,18.8-18.8v-73.527C273.824,152.933,265.391,144.499,255.024,144.499 L255.024,144.499z"
+ id="path31" /></g></g><g
+ id="g33"><g
+ id="g35"><linearGradient
+ id="SVGID_2_"
+ gradientUnits="userSpaceOnUse"
+ x1="225.1929"
+ y1="152.499"
+ x2="225.1929"
+ y2="247.6265"><stop
+ offset="0.0123"
+ style="stop-color:#C1D72F"
+ id="stop38" /><stop
+ offset="0.1394"
+ style="stop-color:#BCD631"
+ id="stop40" /><stop
+ offset="0.5859"
+ style="stop-color:#AFD136"
+ id="stop42" /><stop
+ offset="1"
+ style="stop-color:#ABD037"
+ id="stop44" /><a:midPointStop
+ offset="0.0123"
+ style="stop-color:#C1D72F" /><a:midPointStop
+ offset="0.3086"
+ style="stop-color:#C1D72F" /><a:midPointStop
+ offset="1"
+ style="stop-color:#ABD037" /></linearGradient><path
+ d="M184.562,236.826c0,5.965,4.835,10.8,10.8,10.8h59.663c5.964,0,10.8-4.835,10.8-10.8v-73.527 c0-5.964-4.835-10.8-10.8-10.8h-59.663c-5.964,0-10.8,4.835-10.8,10.8V236.826z"
+ id="path46"
+ fill="url(#SVGID_2_)" /></g><defs
+ id="defs48"><filter
+ id="Adobe_OpacityMaskFilter_1_"
+ filterUnits="userSpaceOnUse"
+ x="184.562"
+ y="152.499"
+ width="81.263"
+ height="95.127"><feColorMatrix
+ type="matrix"
+ values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
+ color-interpolation-filters="sRGB"
+ result="source"
+ id="feColorMatrix51" /></filter></defs><mask
+ maskUnits="userSpaceOnUse"
+ x="184.562"
+ y="152.499"
+ width="81.263"
+ height="95.127"
+ id="SVGID_3_"><g
+ filter="url(#Adobe_OpacityMaskFilter_1_)"
+ id="g54"><image
+ overflow="visible"
+ width="356"
+ height="414"
+ xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAgEBLAEsAAD/7AARRHVja3kAAQAEAAAAHgAA/+4AIUFkb2JlAGTAAAAAAQMA EAMCAwYAAAXBAAALIQAAEOP/2wCEABALCwsMCxAMDBAXDw0PFxsUEBAUGx8XFxcXFx8eFxoaGhoX Hh4jJSclIx4vLzMzLy9AQEBAQEBAQEBAQEBAQEABEQ8PERMRFRISFRQRFBEUGhQWFhQaJhoaHBoa JjAjHh4eHiMwKy4nJycuKzU1MDA1NUBAP0BAQEBAQEBAQEBAQP/CABEIAaEBawMBIgACEQEDEQH/ xACYAAEAAgMBAQAAAAAAAAAAAAAABAcBBQYDAgEBAAAAAAAAAAAAAAAAAAAAABAAAAMIAwEAAgMB AAAAAAAAAAIGATIDBBQFFjZQMwcRECKQMRMSEQABAgQEBgEBBwQDAQAAAAAAAQIxcgMEEFCRsyGC M6PTNBFBIGFxEiIyE1GB0UKhscFiEgEAAAAAAAAAAAAAAAAAAACQ/9oADAMBAAIRAxEAAADy0npz Z0Dnx0DS7Q9kr0IKcIKeICeICeICeICeICeICeICeICeICeICeICeICfggp2CElQD1aXxOgc+O1s um7kKj5vpObG6d2Q9zspRA9JmSGmCHmWIiWIiWIiWIiWIiWIiWIiWIiWIiWIiWIiWIaYIeJo1sPe 4OK5C2tCVS3OmN5clN3IVHod9EOv6zWb0zkAAAAAAAAAAAAAAAAAMRJnwcVXltVuetyU3chUfp5+ 5YexhTgAAAAAAAAAAAAAAAAABjODUVxZNbnjclN3IVHIjyCx5sKaAAAAAAAAAAAAAAAAAAMZwaut rJrY8bkpu5Co5EeQWPNhTQAAAAAAAAAAAAAAAAABjODV1tZNbHjclN3IVHIjyCx5sKaAAAAAAAAA AAAAAAAAAMZwautrJrY8bkpu5Co5EeQWPNhTQAAAAAAAAAAAAAAAAABjODV1tZNbHjclN3IVHIjy Cx5sKaAAAAAAAAAAAAAAAAAAMZwautrJrY8bkpu5Co5EeQWPNhTQAAAAAAAAAAAAAAAAABjODV1t ZNbHjclN3IVHIjyCx5sKaAAAAAAAAAAAAAAAAAAMZwautrJrY8bkpu5Co5EeQWPNhTQAAAAAAAAA AAAAAAAABjODV1tZNbHjclN3IVHIjyCx5sKaAAAAAAAAAAAAAAAAAAMZwautrJrY8bkpu5Co5EeQ WPNhTQAAAAAAAAAAAAAAAAABjODV1tZNbHjclN3IVHIjyCx5sKaAAAAAAAAAAAAAAAAAAMZwautr JrY8bkpu5Co5EeQWPNhTQAAAAAAAAAAAAAAAAABjODV1tZNbHjclN3IVHIjyCx5sKaAAAAAAAAAA AAAAAAAAMZwautrJrY8bkpu5Co5EeQWPNhTQAAAAAAAAAAAAAAAAABjODV1tZNbHjclN3IVHIjyC x5sKaAAAAAAAAAAAAAAAAAAMZwautrJrY8bkpu5Co5EeQWPNhTQAAAAAAAAAAAAAAAAABjODV1tZ NbHjclN3IVHIjyCx5sKaAAAAAAAAAAAAAAAAAAMZwautrJrY8bkpu5Co5EeQWPNhTQAAAAAAAAAA AAAAAAABjODV1tZNbHjclN3IVH7+HqWTO1uxMgAAAAAAAAAAAAAAAAAYzg1Vb2NXB5XJTdyFRx5G jLc3XG9SS2MgAAAAAAAAAAAAAAAAD4+ohqq47GvTa3JTdyFR830nNm/7qp+gLVk8fuDcZgehLRBL RBLRBLRBLRBLRBLRBLRBLRBLRBLRBLRBLRBLRBLRBKQohP0MbkT40OcG8uSm7kKj5vpObAJm45sd n98SO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3xxI7PX84JcQAN5 clN3IAAAAAAAAAAAAAAAAAAAAAAAf//aAAgBAgABBQD+G3//2gAIAQMAAQUA/ht//9oACAEBAAEF AFgq7/bL9narGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxn arGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qx narGdqsZ2qxnarCYVyhn78PQNo/MCUmJhssm48QEScNrGJGEMQgjEIIxCCMQgjEIIxCCMQgjEIIx CCMQgjEIIxCCMQgjEIIxCCMQgjEIIxCCMQgjEIIxCCMQgjEIIxCCMQgjEIIxCCMQgjEIQakYQOlI bGTCajw2R5SPLm/KK2gegbR+LVYzzDZGzFKyBaysYS3FYGSBBQkFCQUJBQkFCQUJBQkFCQUJBQkF CQUJBQkFCQUJBQkFCQUJBQkFCQUJBQkFCQUJBQkFCQUJBQkFCQNkCA9uK0R7WVrJ+ykMy6WI8BrW NY0IraB6BtAsdqbMHtttYxkvKFKxhCs4JpCtExKFMy5W5jWX22NgRAitoHoG0SsBsePZZFhSSkuw peFm5dhi3qRKYk1BbAjoraB6BtCcl/8ASYtUBjCkL8Lwp2fS3WCxpVDA/wA5lFbQPQNoShGNLbif CcM3+roX9VQz4ZFbQPQNoSLP0t7P04e5uql5FbQPQNoSPXIOcPc3VU8itoHoG0JHrkHOHubqqeRW 0D0DaEj1yDnD3N1VPIraB6BtCR65Bzh7m6qnkVtA9A2hI9cg5w9zdVTyK2gegbQkeuQc4e5uqp5F bQPQNoSPXIOcPc3VU8itoHoG0JHrkHOHubqqeRW0D0DaEj1yDnD3N1VPIraB6BtCR65Bzh7m6qnk VtA9A2hI9cg5w9zdVTyK2gegbQkeuQc4e5uqp5FbQPQNoSPXIOcPc3VU8itoHoG0JHrkHOHubqqe RW0D0DaEj1yDnD3N1VPIraB6BtCR65Bzh7m6qnkVtA9A2hI9cg5w9zdVTyK2gegbQkeuQc4e5uqp 5FbQPQNoSPXIOcPc3VU8itoHoG0JHrkHOHubqqeRW0D0DaEj1yDnD3N1VPIraB6BtCR65Bzh7m6q nkVtA9A2hI9cg5w9zdVTyK2gegbQkeuQc4e5uqp5FbQPQNoSPXIOcPc3VU8itoHoG0JHrkHOHubq qeRW0D0DaEj1yDnD3N1VPIraB6BtCR65Bzh7m6qnkVtA9A2hI9cg5w9zdVTyK2gegbQkeuQc4e5u qp5FbQPQNoSPXIOcPc3VU8itoHoG0JHrkHOHubqqeRW0D0DaEj1yDnD3N1VPIraB6BtCR65Bzh7m 6qnkVtA9A2hI9cg5w9zdVTyK2gegbQkeuQc4e5uqp5FbQPQNoSLlvb+nD3N1UvIraB6BtCTN8Jbj /ScM3+rob9VQ36ZFbQPQNoTUx/xGtcdjSlb9ZwsRvwt1jMYVRR/+5hFbQPQNokZinmbPOsaWVjsM XhZuOwpbxOsYWcjtjzCK2gegbQLDdv8ANtuuDGsgTJTMYZjeCaZjBHmSlZcbgxjL9dGxDBFbQPQN oDGtK2z31pBJXYrWQLmVrCz5Whk8QVpBWkFaQVpBWkFaQVpBWkFaQVpBWkFaQVpBWkFaQVpBWkFa QVpBWkFaQVpBWkFaQVpBWkFaQVpBWkDZ4gNPkYI9zKxk7dysZdr80zTGaZoRW0D0DaPzK3Oalmyy oYwEVEv8yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGV S4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXB1RL/JlUMMyauU1Mt/KK2gegbRxaK2jj/wD/ 2gAIAQICBj8AG3//2gAIAQMCBj8AG3//2gAIAQEBBj8Ar2djdfxW7G01az+Ok74VzEcvF7FWJ73Z o+M97s0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+M97s0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+ M97s0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+M97s0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+M9 7s0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+M97s0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+M97s 0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+Ms7O7u/5Leq5yVGfx0m/KIxzotYixTC5kpbbfsfFJir 9/0EWo74+5qHH8y/3IO1Ug7VSDtVIO1Ug7VSDtVIO1Ug7VSDtVIO1Ug7VSDtVIO1Ug7VSDtVIO1U g7VSDtVIO1Ug7VSDtVIO1Ug7VSDtVIO1Ug7VSDtVIO1Ug7VSDtVIO1U4fmT+5803fP3Kn+D4qsVP v+n2LCd22/C5kpbbcUqVkX4+jf8AIiI34QTgQIECBAgQIECBAgQIECBAgQIECBAgQIECBAgQIEBe AqK1FRfuFqUE/SkWf4PhY4WE7tt+FzJS224JWqJw+f0ov/YnATgcMi4i8BeAtdifpX9yf+4WE7tt +FzJS22jKSfVeP4DUROCCcMmXgORU4KPpL/qvD8CwndtvwuZKW20dUVIcEE4Hxkyi8BHon7uC/2L Cd22/C5kpbbT5/8AoTKFG/iWE7tt+FzJS22nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttp zKJlCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZRMoUbMWE7tt+Fz JS22nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzKJlCjZiwndtvwuZKW205lEyhRsxYTu 234XMlLbacyiZQo2YsJ3bb8LmSlttOZRMoUbMWE7tt+FzJS22nMomUKNmLCd22/C5kpbbTmUTKFG zFhO7bfhcyUttpzKJlCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZ RMoUbMWE7tt+FzJS22nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzKJlCjZiwndtvwuZK W205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZRMoUbMWE7tt+FzJS22nMomUKNmLCd22 /C5kpbbTmUTKFGzFhO7bfhcyUttpzKJlCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2Y sJ3bb8LmSlttOZRMoUbMWE7tt+FzJS22nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzKJ lCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZRMoUbMWE7tt+FzJS2 2nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzCZQo38SwndtvwuZKW20dTVfvQQ+cmUXiI xFhxUsJ3bb8LmSlttGVPp8/C/go1fkTjky8RyqsB9T6KvD8CwndtvwuZKW23BKNR3wqftX+qCcRO JwyLiLxF4i0Ka8V/cuFhO7bfhcyUttuCKi/CpBRtOs74cnBF/qJ+oiRIkSJEiRIkSJEiRIkSJEiR IkSJEiRIkSJEiRIkReIv6hadFfl31d9EFc5flViuFhO7bfhcyUttv2ERrvzNT/VT4qIrf+TqIdVD qodVDqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTq pqdVNTqpqdVNTqpqdVDqodVDqC/xorl0F/O74av+qfYsJ3bb8LmSlttyywndtvzD/9k="
+ transform="matrix(0.24 0 0 0.24 182.5615 150.499)"
+ id="image56"></image></g></mask><g
+ opacity="0.35"
+ mask="url(#SVGID_3_)"
+ a:adobe-opacity-share="1"
+ id="g58"><path
+ a:adobe-opacity-share="0"
+ d="M184.562,236.826c0,5.965,4.835,10.8,10.8,10.8h59.663 c5.964,0,10.8-4.835,10.8-10.8v-73.527c0-5.964-4.835-10.8-10.8-10.8h-59.663c-5.964,0-10.8,4.835-10.8,10.8V236.826z"
+ id="path60"
+ fill="#1D2915" /></g></g><linearGradient
+ id="SVGID_4_"
+ gradientUnits="userSpaceOnUse"
+ x1="226.1924"
+ y1="159.7139"
+ x2="226.1924"
+ y2="200"><stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop63" /><stop
+ offset="0.3788"
+ style="stop-color:#F8FBF3"
+ id="stop65" /><stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop67" /><a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="0.4383"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" /></linearGradient><polygon
+ fill="url(#SVGID_4_)"
+ points="221.189,159.714 214.142,180.951 224.048,180.951 214.142,200 238.243,173.61 227.655,173.61 236.978,159.714 "
+ id="polygon69" /><g
+ id="g71"><g
+ id="g73"><g
+ id="g75"><image
+ overflow="visible"
+ opacity="0.75"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="392"
+ height="242"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYwAAAD2CAYAAADF97BZAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAHohJREFUeNrsnYlu40gSBZMU5Z75 /4+dbUsiFwtY2JrqvIqHSEoRACFZPtqk3BV8WVcvAAAACXouAQAAIAwAAEAYAADwWgYuwSp0XAKA wzJxCRAGIgCANf8fIxaE8RIxIBaA8yeMDokgjLkNfLfyzwOAfWTRNX49EkEY5h9N6+sIAuC8/++n mXKZjJ/5UfIYPuyPRXut9WOEAXDuxOGJYEqKZPpEebyjMOYKoTO+BmEAfIYwSjG0JJFaHm8rjncS RkYE2uutwkAgAO8liezzTPqY3jl1vIMwooa+fi0SRvQzshIBgHMKYwpez5Su3jJ1nFUYLWWlznne KpHW5AEAx5fF5Aijfi0jDy91nFocZxRGJgnUMvDkkf3arDiQCMD+YshIwxKE93xy5CGJ1HFqcZxJ GEsE4b0WHa3JA2EAHEcYU8PjlJREdESp47TiOIMwIlG0yqEPPl4iDmQBcCxpZMtOmWNs+Fy2n6M7 kzSOLIy5oogk0SsfZwWCNADeSxhZMYzGYy2I8uslIY5TpY2jCiMzsikjCO2xDz4fiaNFGggDYD9h LJGF9uh9rq8+7hxxWLI4vDiOJozsKCdPFJoc5ry2JG0gDYDzJouxOrTXLJHUsuiKz4uROurS1GHL VEcShpcqNGFoZSZLBBfla6zXO0MeGXGQMgCOkyyyopgCMfzveDifs44yRdQlLKtEdei0cRRhaLKI UkXZoFtCeH7uUn3dJfF9UV8HwgA4tzDG6nktjYfy/OF8zhJLZ0hjEns01SHTxt7C8EpQmdKTlhQu lSguxmu1TLSfNac8hTQAji+MMZEsahHUx6g8743v1aQxOudzSGnsKYyoBOUlCk0StQQGRRrW4aUO bWRVpj8DYQDsI4xaFJEwPFk8FGHcDYGUR1/Jon7uzf84bIlqL2FkS1BWP8XFSRKDIozBEclgiMPq CJ8rDSQCsI0cWmWRTRaWDO7BYy2OXhFH2SE+BudymLSxhzAsWbSkCk8S3vNBkUdGGJE06pTUIgTE AbCOKLRylCYNqxyVTRV347gYX9sr4nhUotDKVPXw292l8WphtMrCSxWeEOrjqghDE0hUlrKkYaUL ZACwj1S8lBGli1oYWpL433Eznl8qWdxFL2trfRsiev/GIaTxSmFkJ99po5+0RBHJoXy8Bt+j9WmU /743qU/E7vxGGgD7JBBrCRCvs9sqRd2NZHErRDEY4iiTxV1p72ppiPx/IqDVr7GbNF4ljBZZaKnC E0UtBu3jq5E0PGFcgpJUH8gCcQDsJworXWQTRiSMWyWM5/Oheu3ZltzFLmk/lPbhkCOoXiEMreHM 9FVYsrhWz8vjS3lNE8dglKiyHd9ewqAsBbB/KcoThjbvwhpCG6WL8vgu2pRb0XbdnGpFKY26vbCG 3u4mja2Fkemz8EpQZWOulZq+gkdNIlFZaq4wBGkAHFYWIm2d3lG6uFeiKNPFt9Ku3ESfEOyJY3TS xi7S2FIYc2ThpQpLFF/Bx6U4auFckiWpFlkgCoBjiCPb8T01Jow6WVx/JHEtZPEt+oCaaD6XRUYa b1OSapXFEJSdsodVporKUZlRUpEsEAbAcaWhpYx6WK02lPamSKOWxVBIo1UY3QJpbC6PrYTROhLK SxW1LH5VUvhlvG5JQ0sXWv/FReYNp0UaAPvKYq4wxoQwvIQRlbojYayRNDaVxhbCaFmKXJNFPcrp q5LFL0UUGWlE6UJ7Y7MT9hAGwDmFEaUMb/5F3X/hjb70hulnZVFL4/Sd3l2iLNUHsrBKUL+M48v4 2EoX0ZvqlaIEYQCcWhalNKwNkrz5GPeftuRWtCk30ed8aUsYzU0WdZoo25ZaHJsJZMuSVMs8i6iv opbEX4FAnt+jdXZnZaEtby6IAuBtUsac/oxaGoPy6A2mWZoupiJldK8uTa0pjLmlKK2DOxLFX8br WllKG5Lbsn4UO+0BII1B/uzLqG9Av+XPKQHZNsU7H22IcF2e0qRx2ISRkYU1g3sISlB/KY9/OUlD 67vIyOKSKEO1JAzEAbCfKFqF4c0CL1PGUKSNm9hr1LXchHYJWYjoy4RYZSnZQhxbz8OIRDEYwvhl SOIvQxh/KenC6+hu7eSOpIEwAM4hjCXSuBRp47lW1F1p16wtoFvShSYLa++MUV7Un7GGMFpKUV66 qPssaln8bUijFoY1u9vbOGnp8NkucX0A4LWyEGmbCT6KvnzIWMjiUcgjsyGbdQOa+V0nyW/p2r2i NLVmSWqJLLR0Ycnib6ck5U3S85JFy8KCSzq5kQfA9pJokUX52AdJo6/EkVmwdM6ci2nG0TmyWE0c S4URDaPN9ltkZPG3U5bS5l1kFhecu2w55SeAfcl0FncJgXh9G30hivqxXIE2Gl3ZO6KIymPZo0xI m/VjrFWSmjsqanBkYfVd/K2kC6uj25JFZ7yxraJAEADHF8pUvTYFjbZUd+5T0UY8hfEQfapAZnRl lCpG0eeHaB/XKcOS5CriGFZ6Qyx5aPMuhiBZ/FJkoaUMTRblMNpoFvfS3fOQBcB55DEp/3e9DuJa GE9ZWO1bpvRkNeZWp7u1Z0emNDUp579YGsOCNyBKF9HIqEFp7K1U4Q2lteZcWOu4tM6xoAQF8H7S EKN0Uz9qd/B90Xh3ku+r6IxUMTpHuWWsdvRGacrr09g9YWTnW7RM0rPKU9oyIN7CgpYs1twxD3EA HJcp+f9UuxPvFGnMFUUXpANLGJ5Qyu+t/+1Ddnp3iXKUNu/iy0gYvyQ/Qa8cGaUt+5FJFiJtI5+Q A8D50kVGIpNy0zgtkIYY/0b5PLOnuHZcqpTRK0nIK0+9TBjZLVfnzOrOJI0vQxbafAuvzyJbfmK4 LMB7JAzv/+zU8H+9lEZL2zApopgMcURHVJrqRO+72a0Pw0oX0YZIVsKIylDWkuVav0UpK2upj7mi QA4A504YnkCmoLpQfm/r3hbWarmRHJ5rV3kpwytNaalqljiGhRc+U4qKNkb6ctJFnSa0uRaaLC7J ZEEZCgCBRCOoLHFMxd19JuVEndyeMLIpo98yZcwtSWWXL5/bf/El9kioL7H3tYhmW1rpYm4pCgDe RyCt4pCigRaxZ297w2fHIFWU6aJ8HOTf61uVbd5mKWOYcVFb08Wc/bm1RQTrVFH3WViy6INUgSgA oEUcWn9HL7kFEOtS1BiIojy+5P97cdSlqUfVDnspYzZLh9Vq+3Rn5mBcE6KwtlgtReEli16YiAcA 64vDayt65XszQ2ejhFFu3FTuxfFQksYo+kitXfowOrFHSnmlqGgLVi9daEt9ZCbmibAzHgBsK46u kIFUlY3pp416CmNIlqNulShuRVtYbuB0r26aR6MsNYm/d8aqwpi7DEhm74urU36K9rTQ1p23Fg9E FgCwpjjqmeFdlTK8ctS1eNT2DP+qZPFVSOMm+grcUV/G4s7v1j6MOcuYe3MwNGl8KV8b7cFd/w4i /pR8RAEAc8QRSUOqlPH8+jpljEVJqZbGl5Iq6qPc7e9eScPry5AlKWONeRjeUNpLUI7SEsUg9sxt bwZ3L/RVAMBr04bXCT5Wpam6XF+WpK7y7z6Ka5EqynQxiL2DaC/xaKnNJ+5Fayi1jpQagtKTNXN7 SKSLaClhZAEAa6cNSxrlXX5fpYyxaNdqadyVdnBuyli187ufeaG0foJMyhgMMdSlp0wZykoXIu3b qAIAzE0b2nNtBGl2YdbBaRsHJ2W0rMg9q23sGy5My2S9PnExhsTFyG6BmHkDAQBeLRFNGpeqNOXt RJppG7Wb6i6Qxiz6hpP3RNIHCSNj0swF6WaUopAFALwyZWRvri1xXIL2sWVqQbR67qrCaEkaLUNr 6wtxCWRh7ZVryQFJAMAe0ojazWe7dWlsI6/JhKG1l6KUoma1lf3Ci2RdiGyyuIg+CspbRLA3TD7n jQQA2EIe0Y21Nw1hUB6z0sgkDC8dLRZGNlVkR0hZpSdLFpfkBVhkTgCADVJGZoM5qyLjyaN1o7hV +jH6mTHL2gcjugjWBfHKUN1WJw8AsFG6iGSRLeNn2spoFOlqbWXfeDG6IHK1ysI76cyOeaQLADhr maqfIY2L5Pt5rQ7vzYfVZspUfXC0CsLq5LbGEgMAnC1laP0Z1giqls7ubBl/k07vaOiYtp6TdjKa JKJJJ9n5FqQLADhj2vCG20Y33NlSVNfwu62SMLqkNb2E0SviyMzgXrUOBwDw4pThrY6R6QPOVmo2 7fvNTNzrgs9F9bhIHH3ihC07C+kCAE4mE00ctUCiakymhN+vfZPdz4gm2fHFXSALK0V409pFWPID AM6XNrwUklnANSpZzRlS29x+zllLqiVpZBKHNwoqE6OQBgAcXRTeIoCd0x564siW8K2RUs0MMy9E 9tBOwNv4KFtjQxwA8C5C8drOLlmlya4h5a21Fy513q948llbdo48okglQn8FAJxLCt68Ma1Bt9pD 7fW1O7q7LYQRxausLaPaGivPAsC7yyTbZnZiTznIyENk4UipfuZJtp68VXLyxJGZoEfaAIAzSaJl TtuaCWOVdrJ18UHrJCNZRFErU3ZCCgDwjglDa+u6GQkjszjr5sLIJI2oA8dLGtKYMBAHAJxVDJ4s Mmv1ZWURlaNeKgxJnmBmT9kueZFFKEEBwGdJJjui9CU7j/YrnJTX6Gcn3m1SbwMAOEnyyDT4SxNF tGrtLGF4nc3euN7MbMKsGDIlKMQCAO8kjeyNdIs0Vm0vt+jDiGQS2TVbtgIAOKMkoopMa5uaEc4q 9C+8EF4UmyMpAIB3Tx4tfcGb32T3G52sVzN7iQkBAA4ogJYbbetjbxe9zFp8s8v8/Y4XCQAAkeiN uwSJQiQ3qbn1Jn/zhNHNuDCR5RAKAHyKLDKfjxJG9t/YpdNbpH1/7ZY4BgDw6TKJSvWtW0Espt/g ROes/eQtxYtQAAAOsJFc/6KTmxPJAADAF8RL29F+5xMHAEAGfz5fvHfFFu1tf8ILCgDwbrLIrAi+ xs/+CGEAACCX/FpTm9x4IwwAgPMkka2+/jDCoJwEAPAGbSoJAwDgwxr+owuDlAEAcHJIGAAAgDAA AD6At5jpDQAAJAwAAPikdIEwAAAAYQAAAMIAAACEAQAACAMAABAGAAAgDAAAAIQBAAAIAwAAEAYA ACAMAABAGAAAgDAAAABhAAAAIAwAAEAYAACAMAAAAGEAAADCAAAAhAEAAAgDAAAAYQAAAMIAAACE AQAACAMAABAGAAAgDAAAQBgAAAAIAwAAEAYAACAMAABAGAAAgDAAAABhAAAAwgAAAEAYAACAMAAA AGEAAADCAAAAhAEAAAgDAAAQBgAAAMIAAACEAQDwMUwIAwAASBgAAHDOlIEwAADgUMKYuNQAAOdu F0kYAABwGGGQLgAA1mtHd2tTSRgAAOeThvX58vhYYZBSAOBTJDAl0sXU0EauJhESBgDA/tKoG/U1 GvnVk0a/08UhNQAArJtENqff4KQydbTpyBcFAODA0titA7xf+MtPM09YuwDT1nEKAOCEaSLqw5he 1Wb2K51c5hedErYkUQDAJ0ohalen4KZ9esXN9h4zvefIBQDgE6QxSVyJmSRXllq9xN9vdBEiM2ai FwDAp0hjMm6sWxKGN9oqandTbW+/0clnkkXr8DFkAgBnl8KcG+dJ/AFFU9DGZqUQ3uT3G1yUKGJ5 1pyEkVIA8BkCySSOKHVMiTZ2tfazn3liU/IEopPPXBhkAQDvKIu5N9JTcGx2o903xpLopDIXyJNB JIgp+XsCAJxBHNkb6czN+ZT8Ppl7Q96vcNItJxHZUoKTRBAA8K4CiYbIPp+PDW1t5qb+ZcKQwIxZ C3oJA2kAwLumi0w7OiqSGKWtI3yV9rNPntyc2KQdkRk9cQAAvIscJidZWG3mqHy89IZ804SR6Zix RDFWJzlK23Axz4zIBQDOJBCRuDQ/OqLItKOrDx5asw/DkoRmxFH5mlH8OhzDbQHg3WQRtZuZhDE6 clky9201YURlqDFIGGODGT0rAwCcVRxRKX9MtJ8tJarF7WffcIJZY3mmtKJVFK+s+AYAcBYxRJ+L Sk5jcMzp12iq2myVMFpO0CpXtdoRgQDAGSQSdW5bCePRII1R8h3tL1lLaq4kyhN/SNwhHtkaUQDA UdOFVRXxkoUmh0fQfmZK/SILy/t94mS9dUsiI3on6J209jNF4o5w5AEAZxFHNM/iURxLZbFKGT+7 ltSUSBjRCKjoRB/BBaQjHADOKAmvKqP1UTyM9jFqQz2BaL/D6sJoPenROdHoKC+EVdfLCAJ5AMCR xJGpzHg32I9EW9pSltosYXiiECNWRXW4R0PUmrOoFgDAEdOFJpEoSTyPe/U4VxpzfvdFCSNKGi1W fF6Au7T1a7SsagsAsHe6EMmVo7yb63tSFF5ZalHq6Gc2utnRUZEkshdgSpw8ogCAI6YLWUEUd+fj pQkj3W6uMXEvEkdWEnfR63abxSsAgBeki7k32Hfj0BJH3W5uMlqqbzh5TyCtUSpzEVpKU6QMADhi uvCE8VBuqj1R3IMb72iY7WLmrlabnXuRsWXGng/xZ4KTMgDgiOlCGiowLdKIOr6jzetm7ZGx1bDa TN/F87glLkhLR44IczQAYL90kZnYbI2EqtvEW4M8Mqt/L2of+4YLEfVfaOb0TvJWXIybcWE8e86Z owEAsJUsrOkGmVLUXZFG1D5mb7BXm4vRz7worQkjEsVNsanVqROtzLjYogAAM2URdXA/GtrIW0Ic L524Nyy4UJm+jEdwEerj+vM4/DxeiuN/P6P7kdz487z7ed4rF6P7ea0rfueOv3EA2KAMJZLbEygr iqjNbE0X2u/YfEM9JC9MV/3gLlGWsmpz1vH98/uUx70QRl8cXXFIJY5IGoI4AGBFWbR0cFtTDeo2 8ltpH7W+jUyHt7fH92YJY3JEkZFFfVG+fxLFd5EsbkXCGKqEEQmjThmlLOp0QdoAgLVkUYujbrSt AUC3QBLWa9mEYW0V8fKSVH1xygbbGjJWlppuijQG5Yhk0SmJoa+k4EmDtAEAS0RRPnorz1ojoer2 sD40aUQDgzJbts5KGUPDxeoco0bjiy9KuhgUWVyLz1+MhOEJoyt+v1H5Gk0SpA0AyIoiksUYVFse jizqR00ac6YeTMnzWj1haHfpVsdOb0SwoUgadbK4FsmiTBnZhNFVKUNDEwdpAwCyohCZtyzSwyhD 1cdvI2lEKcOTxSpTEJb0YZSNb1mailLGUxpRGWqoRKHJonMa+k7aO8ERBwCiiEShVVesEaLlTbM1 2Oe3IwpLGLdkyvDKaZsnDE0cXSGLTMrISKNMFhdHFpYwpkIWnZM4InEgDwBEYcnCWnVWW+LDE0Ut jUgcmc7uVdeQWiIMcS5iJmXcqpLTt/w5IqpFFlGjXs/b6BrEQeoAQBTRpLxphiwsUXji8EpSWv+F yEqd3XOFMTl34V7KuFelpUtwaGWovlEW5UXqg5SRKU1NhmQA4NyCyIpCjGShDZ3V5lV8B3L4bXzu OyhFeSOkWs5/s4RhDVEt7+QfRUNfSqNMGV6qqKXRBymjlsTFKFPV3zsVH7eUpqagzAUAxxRDNmFk k8XDSBfaCKhaCv/5OTxpRB3eXt/FquvtrdGHUd+p18t2PIqGXytNXZTk4U3Sy7zxtTiespjEHmk1 JctVmWQDAOcSibXQamYDJGvobCmL34njP+L3YWRLUasOpS25zPy+srPZKhFpX+Md3pDZqA+j5Y9k 6UXrFn4eAPZPGNnyU3borCaLm1KG+k+VLLzDG17rSWOTdLFEGCJ+B7IllEgCEryWbaCjWZlrxVlE AXA+cSyRxST6sFlNFjdHFnU5yhPGb0cW1gipTSofwwpvRl3S0dZ+19JEn0waljCiIW7Px6EqS12K z2n9IyL+pEBGTQGcUxaZ5T2iDm5v8yNtUp7VZ/FPQ7Lw9gpqWdJ8t+XNPVlIUhqZklSXSBGj2JNn roU0xh9ZjIU4ns9HRxzWo9dBjlAAjlOGmgJJiCzbz8KSxbfofRSRLH47Zaho7oXIhpvKrTUPo1N+ wbFoOLPSyAgjMw66vJClLMpjlD9HYdXikMSjJocu+SYhFYBlMmhJFa2y0EZCZWRxE33IbC2Hf6rH WhatI6M230RurZKUNcy2FkerNLw33lvw6/mmXos3tlyj6iH6aKw+WRaLZIEgAPYTSKs0rEUEvdFQ 1uZH2qQ8r5/iH4k7uVtGRmlltdVYM2FYb9RYNbgZaXiNq/amWtseatJ4iD9JcKk0WjrnAWB7aXhr QXk3oJP4o6G0mdzakh9WZ/c/Ys/DiFanrTdM8q7BoUpSUWmqbOSz0rB+flSGeiSkcRF7rw1vhrlI bhgx0gDYVxhT0GjWd+FjsmJxF31TuGg2dzSk9rdRjlpj7+7DJYxsaapMG1oDG02Es5ZR90RRvsHP pdOfW79mpZFdUh1hABxfGN5EvKws6r6Let8Kq/8imt3tLWWe2fNis1LUFgnDeyNHpeF8JGThJYxo 8kx5J3AtJHFVUoa1LIkmjH6mMJAFwPbCyHZyjzOqFlay0Pa1aJnR7Y2KinbV82SxujS2Kklpo4Qm RRwtPzsShZUqvooL/0wX9QZN1gq5njhE2kZSIQ2A7WQxNT5qZai6P3SU3G559RIgVsqwEoW1wGA0 jFacEtQpEoYnjXLOQ7bxzAyh9UYtfBXiuMq/d/UbnKShiWOuNJAFwHGkYQnj0ZAublU5yts5L1qy /DuQRVSCispzpyhJlfLwImGLLKw+DC8yluIYKnFo0uiN8hTSAHgfYYwSz+HKlqIyW61qaULbqzsq Q2X7LTaTx7DRm9gF4sjKQpKi8IRx/XkjalnUfRmeNCxhRP0ZCAPgGMLwZDGJP4imlsXdKEdF+3Pf xO+rqDu4DyWLLRNG1J8RScMaAjc69UUrXVyrhOGVpYbGlIEwAM6TLsZkwshULW4N0rgpj9oM7nr4 bKss3qIkNVcaIvl16LWRUc9SVJkwhiphWH0ZLSlj7dngANAuCi1R1M+z6WIUe85FnTK+FWnclBRR J4rspLwWWWwujuGFb3KLNDL9F1Z0/DJKUbUwWstSnjSQBcDxpRHJIprRHaWMmyEIL1FEqSLb0X36 Tu9SFFlpTOKvSZ8dVntVRKEJo+78tvYWvyQSBsIAOFZJKhKG1p6MTtXCGimlPY9E8RB9BvdDkdok L1qN9ggJo0Ua2T0vvIRxlT/7MKwSVDS8NprINzdlIA6AdUQRJYy6HOUtLGi1KZnSlCWSjChaS1C7 yGKPklQkDS1teEnjUr3JQ/H4nKh3q4RxUaRxCRJGZngtHd8Ax0oYmXJUZq+LaB0p77WHxP0UD4nX htqlz2IvYWSkMUnbHhhjII5aCjcjTXjlqOxcjEgaiAJge3FkN0NqmYORKU9ZcmhJFNYM7sPI4tXC mFue0t703hFH+ca2pIkoXWRkkU0ZHogFoK1BbNk9L+rH8OZ5ZYRgPc+Iwis/7S6LPYThSUOTxyh/ 7hNei6N+oy/y7z0v7skk4fVdZIWxRBaIAmC+OLKyyHZ+eyth3wOpRENkWzc/OoQs9hJGNmlMSmNc v+F9lTZ6+fduehdFHPXn+oQoMsIQsffKQAwA24ukRRqZlOF1hkevWf0To1IWkzPIYk9hlCffGc8l SBudkjaejXmdNHrjMRLEnHSBMAD2k4WIP2CmRRqePEbxl/Cw0kRUftp1nsWRhdFaotI6xbW00RWl qmfi0NJDS5pAGADnF4ZIbk0pSxjWx1lJjOL3URwyVRxNGFrasGRRp43668dKFmVD/0gKwtqiNdrn m/kXAPsJQyS3rPmkNN7185bDks00s/R0WFkcSRiiJAxLIJNxh1+nkzFICr3zemY01JzlzZEGwD7C kERpKtv4j4mUUm9L3ZImpqNe+OHAfwzRwoWROLoqcXTiL1MepYmlu+0hCoDXlaZapZFJHlMghslJ FJnf7dCyOKoworSREYcYAukqeWTkQKoAeO+kIQl5eK+PkptDccpUcRZhiPgjqTKd4p2TRGSGIJYI A2kAbC+LrDBE8qOpWo45SWI6y0UfTvbHEZWp6mSSafQzH4vkJ+chDIB9hRHdxWdGKUWL/0Wd2G8l irMJQ5OBKOnDk4bX6GdSw1qLCyINgNcKo0UakUhE5o1yOrUozioMcWTRkjqyKSGbJOjgBjiuSLyG u6V/YU5fxFuI4szC0N6MOaljmiGDTOkJUQAcM31MM59PC37GW4jiXYShiUMkP7JK+16SBMBnp45s w9/6McI48B9GlDrq2eNTQjgIA+D9hLH11yCME6cOCdJHy89AGADnFEbm89PCr0UYb5A6ZIFAsn8o SATguIJo+fppxX8HYZz8D6n75DcfAGE0ff3HtxEDf1ipdNDyh9LxhwVwOmkgB4Sx6h9Kxx8aAGJB GLDmHxb9FwCIAGEAf6gA8Ln0XAIAAEAYAACAMAAAAGEAAADCAACAs/JfAQYAL3iXmIlSiu4AAAAA SUVORK5CYII="
+ transform="matrix(0.24 0 0 0.24 179.2061 198.1514)"
+ id="image77"></image><g
+ id="g79"><radialGradient
+ id="SVGID_5_"
+ cx="225.1929"
+ cy="226.1387"
+ r="30.8299"
+ gradientTransform="matrix(1 0 0 0.75 0 56.5347)"
+ gradientUnits="userSpaceOnUse"><stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop82" /><stop
+ offset="0.4828"
+ style="stop-color:#FDFEFB"
+ id="stop84" /><stop
+ offset="0.7611"
+ style="stop-color:#F8FBF3"
+ id="stop86" /><stop
+ offset="0.989"
+ style="stop-color:#F2F8E8"
+ id="stop88" /><stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop90" /><a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="0.8025"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" /></radialGradient><path
+ fill="url(#SVGID_5_)"
+ d="M186.706,235.825c0,5.965,4.835,10.801,10.799,10.801h55.374c5.965,0,10.801-4.836,10.801-10.801 v-19.373c0-5.965-4.836-10.801-10.801-10.801h-55.374c-5.964,0-10.799,4.836-10.799,10.801V235.825z"
+ id="path92" /><path
+ fill="none"
+ stroke="#EDF5E5"
+ stroke-width="5"
+ stroke-miterlimit="10"
+ d="M186.706,235.825 c0,5.965,4.835,10.801,10.799,10.801h55.374c5.965,0,10.801-4.836,10.801-10.801v-19.373c0-5.965-4.836-10.801-10.801-10.801 h-55.374c-5.964,0-10.799,4.836-10.799,10.801V235.825z"
+ id="path94" /></g></g><path
+ opacity="0.74"
+ fill="#FFFFFF"
+ a:adobe-blending-mode="lighten"
+ d="M263.623,229.595c0.037-0.364,0.057-0.734,0.057-1.107 v-13.375c0-5.965-4.836-10.799-10.801-10.799h-55.374c-5.964,0-10.799,4.834-10.799,10.799v7.324 c7.545-1.012,15.699-1.566,24.213-1.566C231.959,220.87,250.812,224.252,263.623,229.595z"
+ id="path96" /><linearGradient
+ id="SVGID_6_"
+ gradientUnits="userSpaceOnUse"
+ x1="225.1929"
+ y1="204.3135"
+ x2="225.1929"
+ y2="246.626"><stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF;stop-opacity:0"
+ id="stop99" /><stop
+ offset="0.0141"
+ style="stop-color:#FDFDFC;stop-opacity:2.231669e-04"
+ id="stop101" /><stop
+ offset="0.1344"
+ style="stop-color:#BEBEAF;stop-opacity:0.0148"
+ id="stop103" /><stop
+ offset="0.2565"
+ style="stop-color:#94957C;stop-opacity:0.0297"
+ id="stop105" /><stop
+ offset="0.3796"
+ style="stop-color:#747759;stop-opacity:0.0446"
+ id="stop107" /><stop
+ offset="0.5029"
+ style="stop-color:#5D633F;stop-opacity:0.0596"
+ id="stop109" /><stop
+ offset="0.6263"
+ style="stop-color:#4D552E;stop-opacity:0.0746"
+ id="stop111" /><stop
+ offset="0.75"
+ style="stop-color:#414B23;stop-opacity:0.0896"
+ id="stop113" /><stop
+ offset="0.8742"
+ style="stop-color:#3B461E;stop-opacity:0.1047"
+ id="stop115" /><stop
+ offset="1"
+ style="stop-color:#38441C;stop-opacity:0.12"
+ id="stop117" /><a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF;stop-opacity:0" /><a:midPointStop
+ offset="0.2901"
+ style="stop-color:#FFFFFF;stop-opacity:0" /><a:midPointStop
+ offset="1"
+ style="stop-color:#38441C;stop-opacity:0.12" /></linearGradient><path
+ fill="url(#SVGID_6_)"
+ a:adobe-blending-mode="darken"
+ d="M263.68,221.954v13.871c0,5.965-4.836,10.801-10.801,10.801 h-55.374c-5.964,0-10.799-4.836-10.799-10.801v-13.871l0.038-7.704c0,0,0.923-9.937,11.173-9.937h54.962 c0,0,10.063,0.328,10.801,10.799V221.954z"
+ id="path119" /></g><g
+ id="g121"><g
+ id="g123"><image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuJJREFUeNrsl9trE0EUxjO7m5vW tKFN1RqLCmqlIvjgkz5I/cOFIqLggw9KsRHxUo1IdEtactG9+A1+A8dxNrsxK/rgwI9lt5ueb875 ZuZspfJ//Bhqjvc0AfCIHClIQEzSMkUoBqyCJbAKWrxXQoBmBL6AQzChmGQREWbmNQY/DS6Aa6AL mtZvdcDPoEcOQEgxUV5mVMYzH5wCZ8FFcJ0CLoN1UHeIGII34AV4BvbBW4qbzsqKctzruq+ALXAL 3ABXwAafNyjS9sQ3cAwG4BXYA0/AU/AejLOE+I4MtME22AH3wE2wyedNivSFQT3eB/y79kwHnGE2 v4IjinCaNrBEtJiBu2SLs686VkRWGRt8/wTL5jFwxIxMbSGB+Ac1qtcluEMBbWslFDV7QBFdlmBE bwxZtthVDn1dpgF3WIIOhakF9iCf2ajQK32W5hcRJgvnmYHb9ECzQAnyhif8o7PxkWImsiQeRSyJ fWCjJAGy5G2usKtgzc6wx5dWxT6wYhm2jKNBm/UcV90m/aLsdLVoonX+QJV8RvmcXNflNVOOKktQ Fz4p+6AMrBg/GUeeFWUHd51HyuXevz7+GRELNSRzjMwYnmhI5Laa/gEBYxEjskVE7Ih67AeOi3ZE BYc55j+xxzjgpBMpImZL1mNDMuDxm5aYBT2x1+wx+vZJ6lt94kl2Ux1uWl4JWZhy9g/AQ/DOPjt8 q0ULuLebhiRYYO8wPUTIdm+X1zDrKE/FKjH95TL3eP83MiIF7FHAY2ZkYpfadxhoRE80WJ66EKIK BE9YAiPgPkW8dPUSFUfDGnMpHVmKvQJCEoofcsamBLs0fOgSUMnomo2QQ66UAbMTi4+hmOk2mGZW B39OE+rgj5iBcNb3h5qxk9boDb1SLrEh2c75+NlnCfT1A4OP8nZiVeAT0IhZY0Ni+gHP8oEpQ59Z HHP2uRtfkeUnxTj7AWHqMU0ZiRVX2ld5kZ4jnSewHN8FGACSOOKkAlOGAAAAAABJRU5ErkJggg=="
+ transform="matrix(0.24 0 0 0.24 199.0298 216.5547)"
+ id="image125"></image><g
+ id="g127"><radialGradient
+ id="SVGID_7_"
+ cx="202.6289"
+ cy="219.7041"
+ r="2.9995"
+ gradientTransform="matrix(1 0 0 0.75 0 54.926)"
+ gradientUnits="userSpaceOnUse"><stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop130" /><stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop132" /><stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop134" /><a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" /></radialGradient><circle
+ fill="url(#SVGID_7_)"
+ cx="202.629"
+ cy="219.704"
+ r="2.999"
+ id="circle136" /></g></g><g
+ id="g138"><image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAtFJREFUeNrsmP1LFEEYx292Ts3V 9ujFrCiwFyPShH4I+imoiPqbhYKIoKigN0W8SulNIrOU63S921u/A9+BYdm9mbndg4IGPiynuzOf eea52WeuVvvf/Joo8VwA6rxm+0lBD3R5TYctpu6XYBRE4DiYzMhpqRbYBDtgHyQ+gsIzQmMUOg3O gzkwzciZTUXqO1gCH8E3CsauERSOUiOgAc6AC2ABXAYXwZECsV/gPVgBb8AH8AVsg45NTjhKTYGr 4Aa4AmYZqYjLmpdj+4ySilwTLIOn4C34YZOTjlLXwH1wC8xzKSMureQym0g+O85ITxP1uU3hPS6r t5j63zFK3QM3uYwNQ0g45KUSPMSJNLjsLS71blHUZJ9Ox5lDSuo2k32SHYsBvslKcAIc5jJvUK7r I1ZntK6Du8yryBJhW9P73hi3jg2ym7ek0hKtO0z4E5xx2RYYOany7DPYyotaUPCwyoVL3KemKpLS TX+h5jhGI88jT0x9/U9yrzoHwoL7ykQtZN8LHGvURUxy05xhntWH8I7WOTzDsaSLmOAMQl6DIYgF mTGEi5iWEyWqD9dtpHCMoPaXtn9KrHSR59CsYxSJ6SKv1e9FW6L1MmM4iXWMIu8ri7u04mjF7HuJ Y3VcxLqciaqfVlnYJRWKJexzlWNs5r2SZMGMNOp1cRYc5atEVBCtPVazD8AzHzHdQUKZUyzywoIT kY9Uh9XrC4o1WTimPmI9ouv9iAXfIHKm1GtKvSyKlq2C1Una5sMTLBRHPN4MOvIxpV6BRfCEJU/s W8Ganf4xzoaCf5dGaS36JHnMKnWNUg/BY35uD1rza7ku5bY4658cMDHkEt6nUZP4TQG1dI/Ic/CJ /SVVHHglS2J94pnluXLecuB9x3Nlk5+3jUlV9hOBMAQjCpn1lMikgFrCdQrtGEKp62CDlCtaMLQc eNu+QmV/7XGp2cyN2rsdCDAAoyXZx8WJpTUAAAAASUVORK5CYII="
+ transform="matrix(0.24 0 0 0.24 213.9448 216.5547)"
+ id="image140"></image><g
+ id="g142"><radialGradient
+ id="SVGID_8_"
+ cx="217.5439"
+ cy="219.7041"
+ r="2.9995"
+ gradientTransform="matrix(1 0 0 0.75 0 54.926)"
+ gradientUnits="userSpaceOnUse"><stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop145" /><stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop147" /><stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop149" /><a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" /></radialGradient><circle
+ fill="url(#SVGID_8_)"
+ cx="217.544"
+ cy="219.704"
+ r="2.999"
+ id="circle151" /></g></g><g
+ id="g153"><image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAttJREFUeNrsmO9r00Acxptc1m6d Fn9M125sU4RVpyjiSwXB/9wXulciiE4dhpUMpwzFUa02XdP4HDwHR7hcLk0mCh58WOnI9548973k uTYa/0e54c15nQ8E8TJ1UpKQ2Z8QJgUtgDa4CC6AliZOiYrBCfgGfoLTsgK9OQStgE2wDa6DDv+v hhQwBAdgH0TgS1mBLsIEBV0F18BNcJvC1sCyQdgIHFHYG/AODMAxBSZVhQXgHJ15AO5T2Aa4TMHC 0GMJBXwFhxT2Erygkz/AtGhim1NS1A3wCDwGO+AKBS3QKS+nrlp6eQProKe5G4LvNucCS0+1uXQP wRNwj6JaFkH6SgitNxeJ0BwNueSzMsIC3ulduiX/roJmpp9cWkVQ1CrrqB17ws+TPGdM3y3Rftnk fTpVVlS2ZpN1+qy7znl8V2HSrUvcdbKnutryVRk+63RZd5vzBC7ClFvyMXALbHEDiJreNGpDbbH+ Wp5rJmEd7sQ+n13NCq8uU881WbfPeTquwtq0u1ezW1nXepyn7SJM9dgSCc4oPBTO4Rus9jKJwatZ lGeZy+rYXzH+GWEpXxEq5Kl8VecwBcnURVjMgCcZn5GwsTZH7CJMD3khL5zWLGzKuiHnGZpe5CZh I6bO9wx7v+bN7YYxY70j1o/yEoZpKSdMmntMoMq1tIYlVG7ts/4x50tddmXCEDfgxRETZ1JRWMI6 EesObGFRWCyfaa+oDl8jQd4DscApJUr21S54Dj7wu1JBUW2Ct1rybDDRntditUtPnWor8Aw8Zd2h rXdFQdGYd6WfbPTYnOeeEiSv/cTDyC5FvbL1luspSSXPFUYUmaHu8KS0yfjdMpySYp6QIop6TZdC njEnRTvdpVc8Lt0yBW4wS+04HHj3+Fg4pKARnUxdJnVNBL7hSNal4OxPBFLAZ/CRzumn8NR1wrKR xdfy1KLlwDvmw3RaRlDVX3s8h8dGWiUE/BZgAMf82R9IYLF+AAAAAElFTkSuQmCC"
+ transform="matrix(0.24 0 0 0.24 228.8599 216.5547)"
+ id="image155"></image><g
+ id="g157"><radialGradient
+ id="SVGID_9_"
+ cx="232.459"
+ cy="219.7041"
+ r="2.9995"
+ gradientTransform="matrix(1 0 0 0.75 0 54.926)"
+ gradientUnits="userSpaceOnUse"><stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop160" /><stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop162" /><stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop164" /><a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" /></radialGradient><circle
+ fill="url(#SVGID_9_)"
+ cx="232.459"
+ cy="219.704"
+ r="2.999"
+ id="circle166" /></g></g><g
+ id="g168"><image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAt9JREFUeNrsl91rE0EUxTOzm69a 05YmVWsUFdRKRfDBJ32Q+ocLRUTBBx+UYiOitlqRaEos2UT3w3P1jIzrbHZNVuiDAz9CNru5Z+69 M3O2Uvk/fg414zOCDzSxRwJiEJGkTBGKAatgEayCFr8rS4AwAp/BIRhTTDyPCDPzGoOfAhfANdAF zdT/SMBPoEf2wYBiQldmVAEBHjgJzoCL4DoFXAZroO4QMQRvwAvwDOyCtxQ3SWdF5QiQui+DDXAL 3ABXwDqvNygy3RPfwBHog1dgBzwBT8E7ENhCvJwMrIBNsAXugZvgPK83KdKzGlTzu8/fpWc64DSz +RV8oYhfTetPEdFiBu6SDc6+6lgRWZNo8P4Flk0zcMiMSGkSP+MPalQvJbhDASuplVB0RfkU0WUJ RuyNIcsWucoh15bYgFssQYfC1Bz7kcdsVNgrByzNHyJMFs4xA7fZA80CJcgb2uofycYHihlrh4hF ax9YL0mAGT7LKivsKmjLpLXjplVrH1ie0ryzlkWa9SxXnWR5QTv6ocUmWuMDqlLu8Di5ruk1Vzmq LEGdD5QtQln7yI8YespZUXbwzBi6cgzGsRTx14ZkxvFbDJeI9Laa/AMBgRUjTIsI6Yh69ANH0xzR DMMc8x/pMcTwjNIiIlqyHg1Jn8dvUmIWZGKv6THk/Jh4GWqFE3RTHW5auoQsTDj7B+Ah2JOzI8vU RNxQ2pYh8efYO4yHGNDubfNzkHWUJ9YqMf5yiZ7AmyEjtoAdCnjMjIj5TbycBhqxJxosT90SogoE j1kCI+A+Rbw0XmKaxzQlCXjz2GpOXUBITPFDztiUYJsNPzAC8kQklpBDrpQ+sxNZL0MR020wZlaC P2cTSvBHzMAg/f6hCu6qNfaGrJRLNCSbOS8/uyyBfL5n8JFrJy7a7Solpk1DYrynTvWBKcMBsxhw 9nEZL8S2GNtzuJo6YFOG1oor7a28iOdI8gLb47sAAwCDFN6m03jgxgAAAABJRU5ErkJggg=="
+ transform="matrix(0.24 0 0 0.24 243.7749 216.5547)"
+ id="image170"></image><g
+ id="g172"><radialGradient
+ id="SVGID_10_"
+ cx="247.374"
+ cy="219.7041"
+ r="2.9995"
+ gradientTransform="matrix(1 0 0 0.75 0 54.926)"
+ gradientUnits="userSpaceOnUse"><stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop175" /><stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop177" /><stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop179" /><a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" /></radialGradient><circle
+ fill="url(#SVGID_10_)"
+ cx="247.374"
+ cy="219.704"
+ r="2.999"
+ id="circle181" /></g></g><g
+ id="g183"><image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAn9JREFUeNrsl+lrE0EYxvdKjSZW YxEPxBsVrNdHQTxA/KMFBRGPDwoVVIpoq3jUeJUG25qk2fVZ+A28WTabxG4lHzrwgxw78z7zzMw7 73reBDR/jOcCUREhn21LRCy6osfn0kT4BN0h9oiDoiGmTN8Efoum+CHWEBRvRoSbeZXgR8QZcVEc F7syfTcI/kq8Fgviu1jlv3hcET627xXHxFlxWZwXJ8RMxgmPIKkTn8UbMSdeinfiq1hnmUYS4QTs F5fENQSc4rfUgahgT7TFivgk5sVj8VQs4kqukGiAgCviDiKOijr/BUOWL7t/9uGaVyTEighYggsI uI79NQYfdY9FPF8x/WL2xiJLk9hOoelcJehtcYslqI8hYNDGrvP9G5t1PbtRQ+NIg/W/KWaxNNpk DnK5JZ35TzbuCq70ibAu3BBXxWHW1i8hGbpc0+akNLNuBDAtTopz4kBJAuxEC8cPzIlI88BpOoQl Xw1TuDtLsqvZkxbwUA2FjZxEVJYbdROj4mWOpVuzCMoWYGPlxggm4SrfFmFFJOTzDUi2KFY8KIYT sUoSWRadLRDSM0XPMgVPn4guOT0tSN6KVtHd/w8tYWJfqDHeM+m+jBkTeIEaoEmKTUp0oXD80Kjt kU4PkVSqJWTOhIDpFf5APBFLOOPliYhxZgYR00MKmVEEuKWeQ8Q8ruRe5Xb3po7s5CqvDSjnxhVw XzzjFu3k5XTbuuziNYLvZolCk+KHBU8n8QcBL8Rd8VB8yCto8kTEDNBCTIdBg4wQvyD4L6rsdOb3 xKNhhW44IKm4wZaghSAnoIdrHWhz/m3wlOfiI86OXPJPzMvPxLwG/tcX4u3m2l8BBgBQ/dU5d1Za tAAAAABJRU5ErkJggg=="
+ transform="matrix(0.24 0 0 0.24 199.0298 230.2217)"
+ id="image185"></image><g
+ id="g187"><radialGradient
+ id="SVGID_11_"
+ cx="202.6289"
+ cy="233.3711"
+ r="2.999"
+ gradientTransform="matrix(1 0 0 0.75 0 58.3428)"
+ gradientUnits="userSpaceOnUse"><stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop190" /><stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop192" /><stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop194" /><a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" /></radialGradient><circle
+ fill="url(#SVGID_11_)"
+ cx="202.629"
+ cy="233.37"
+ r="2.999"
+ id="circle196" /></g></g><g
+ id="g198"><image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNrsmM9r1EAUx3cz2XW1 il2wLLagIh5aeilUBC967EX/XA/1It5aUaiC2JNY/EWp0lZkG3c3id+Bz0AIaXayibKHDnzIJfPm s29eZl/Sas3paNeYZ0SHaz5OKmIx5pr+azF7fyh6oi+WxXURZGJZiUScim/iWERiUkWwXTFDlxG6 I1bFBnKd3P1jpPbEvviE4JlvBtueUl1xQ9wVa2ITsdtkzOTmxGTsALE34oP4KH6I0TS5tofUJXFT PBCPxToZ67OlZTUWkSmbsffipXglvos/ZXLGI1N2qx6Kp+KRuFcgVYRh/oJYEgNxle08EUNqsbJY SDAnZa8r1FlYoQzc02sFF5n/W/wskzMlAReopydkaoVtDWocL1bqGtv8Bblx0YSgJFt9xNbJXLfG uZctjQEx11gj9M2YDXCFiVviflmAGeQCYtlt/MxTOvbJmEFkFZqSyu9GNr7xEXNP4gbnVK/mFhZl rUdsd0B3fcQCDs3lcw7PJobJrRH4iE37g24qa6VrBK05HRdiTYjVbvI8xtQ1isSyTd4pE5secW6N xEdslGnyDmhd0oazFRF7j7VGPmIxPdQ+HNMWNzUmBfFj3+7C1YDtMG7RT3UaONMS+jErtC1e05tV EksyPdkAyToHbsqW2e51R7ygs42qNooJaU/oPF2TF8wgl2SkdsVz8Y5sJVXFUvryX6TfNXld5HwE U37cWUbqGdfDsto1njVxQjscQ8jc8+SckO25jqipHTK1i+Ro1peR7FM6pKH7StAhC7uam7CQI+J+ J7RNTb0lU7Vf3+b6hXeuPxH8948qF6Pq+CvAAGGezDColMK7AAAAAElFTkSuQmCC"
+ transform="matrix(0.24 0 0 0.24 213.9448 230.2217)"
+ id="image200"></image><g
+ id="g202"><radialGradient
+ id="SVGID_12_"
+ cx="217.5439"
+ cy="233.3711"
+ r="2.999"
+ gradientTransform="matrix(1 0 0 0.75 0 58.3428)"
+ gradientUnits="userSpaceOnUse"><stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop205" /><stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop207" /><stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop209" /><a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" /></radialGradient><circle
+ fill="url(#SVGID_12_)"
+ cx="217.544"
+ cy="233.37"
+ r="2.999"
+ id="circle211" /></g></g><g
+ id="g213"><image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlRJREFUeNrsmM9LG0EUx5PdjTTW otDYWmoOQm2M/YGXnrxI/3IvnnoRW7TagocotqjQtLYp5pffgc/CECc7u+uGpuDAB2Y32Tff9+bN 7Jstlaa0lXM+E4gIAsd/BqIHpj+cpLBY0Ix4JBbhwYg4I+SvOIdf4jqrwHIOQUvihXgtVsWCQ9gP 8UV8El/Ft6wCyyl+DxH0XLwUb8UbxD0V1RE7ZtCO+I6oj2JPHIlTBPZ94sqe3yIi0hCb4h1Reibm iOK4HDPRuRJnRO+D2BGHRLSXJC7yRGpeNMV7sYXAeQSFCU6FRHIGB56IGveMI/uIGxu5JGFzTN0W wpqIijIsmpDFYZ55hah4xRpxP7MIM4NWWHEbTGGDKY1ybi8VKyVM/l2wYjtM+S1xrvww9x6KulgX K0QqvOOeGafGCnbrjOPScOtmnPA1Hm7Sr+TcjF2Rq2F3nb4zNVzCZsWyWMOr2XFe5WgB9urYX+ba KywkF1bJh8d4WWSrYLdhbdBhmohVrddNtYApLOUZI0jxgp6EMO8YQWlK272wuwobFlHkeVqqMVzC OlaR15mQMO8Yo8L6VpFnypNL0S1YWBe7h4wTVxneiP0RJ+KzaHE9KEjUAHst7J9wnWoqe7z9TVly QL9bwJQOsXOB3X36vbTVhfHqN16Zh49F2xXujK2PnWPsthhnkLYeiz0ziblrVZ55CkV7Ftrk1Q52 z5NmIkowdsUBIrIqz7SltR2la0vUNhxhP3PNP7RCf4CouPIs4jDS9p2U/svj21QfeKf6E8E/+ahy 37K2GwEGAJb/2mQI89WQAAAAAElFTkSuQmCC"
+ transform="matrix(0.24 0 0 0.24 228.8599 230.2217)"
+ id="image215"></image><g
+ id="g217"><radialGradient
+ id="SVGID_13_"
+ cx="232.459"
+ cy="233.3711"
+ r="2.999"
+ gradientTransform="matrix(1 0 0 0.75 0 58.3428)"
+ gradientUnits="userSpaceOnUse"><stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop220" /><stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop222" /><stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop224" /><a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" /></radialGradient><circle
+ fill="url(#SVGID_13_)"
+ cx="232.459"
+ cy="233.37"
+ r="2.999"
+ id="circle226" /></g></g><g
+ id="g228"><image
+ overflow="visible"
+ opacity="0.25"
+ a:adobe-blending-mode="multiply"
+ a:adobe-opacity-share="1"
+ width="30"
+ height="30"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNrsl2lrE1EUhmdLjSZ2 sYgL4lqsYN0+CuIC4o8WFERcPii0YEsQtYpL3cVgW7PMjO+F55ZhmMlMzATyoRceSDKZc957zplz zzjOBCz3P+/xRE34fE6uWESiJ0I+VybCxekeMSMOizkxlbATwx/xRXwXWwiKRhFhd17H+TFxVlwU J8W+lJ0+zlfFmngjvolNrkXDinAJ+6w4IRbFZXFenBLzqUg4ODGR+CheimXxQrwWn8U2aSolwgo4 KC6Jawg4w28mAsGAmuiI3+KDaInH4qlYJyo7QoISAq6IO4g4Lppc8wrSl66fA0TNSQvJE+GRggsI uE74GxgvW8gB/68l7ouojXVSE/s5N9dxelvcIgXNIQTkFXaT718pViMiyjIa8OiZ/N8US4Q0GLEf 2d5iauYHhWtqpu8PiMINcVUcJbduBY3R9poOT4rpJdteRi1Mi9PinDhUkQC7/Cz7aRH2iTB9YIEb /IqPiSmiu0Sza3gZf2qgcC6jEVUVjWbCR83LOR8CqFpAMu07PrxJOMp3ReSJiOnnfYjH5DdK+sgS sUkT+SW6YxASJoYe46OXFtGjp5uB5JVop8/+EVfMxj4xY7w1m/YywtRmGmqhtlNhNMIs+36O2pB2 fYSmUq+gc8ZsyBzhD8QTsWEikyciomjnETFdMMiUEWBTvYyIFlGJ/ILqNRHZy1HeyBnnhhVwXzzj FO06BSHuUcVbON9Piuy7hlvCudnEXwSsiLvioXhnB5oiEREG2ojpYtRLCXEHOP/JlG12fk88yhp0 /RJNxRrbgDaCrICQqHWhw/OfdG54Lt4T2dIj/8S8/EzMa+DYX4h3l13/BBgABM7SO70ZkkMAAAAA SUVORK5CYII="
+ transform="matrix(0.24 0 0 0.24 243.7749 230.2217)"
+ id="image230"></image><g
+ id="g232"><radialGradient
+ id="SVGID_14_"
+ cx="247.374"
+ cy="233.3711"
+ r="2.999"
+ gradientTransform="matrix(1 0 0 0.75 0 58.3428)"
+ gradientUnits="userSpaceOnUse"><stop
+ offset="0.0123"
+ style="stop-color:#FFFFFF"
+ id="stop235" /><stop
+ offset="0.4235"
+ style="stop-color:#FAFCF6"
+ id="stop237" /><stop
+ offset="1"
+ style="stop-color:#F2F7E8"
+ id="stop239" /><a:midPointStop
+ offset="0.0123"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="0.6235"
+ style="stop-color:#FFFFFF" /><a:midPointStop
+ offset="1"
+ style="stop-color:#F2F7E8" /></radialGradient><circle
+ fill="url(#SVGID_14_)"
+ cx="247.374"
+ cy="233.37"
+ r="2.999"
+ id="circle241" /></g></g></g></g><path
+ d="m 529.664,248.155 h 18.498 l -2.809,18.064 h 5.59 37.586 l 2.6,-17.718 c 4.98,-1.091 9.133,-3.455 12.512,-6.693 3.084,4.075 8.566,7.37 18.252,7.37 6.338,0 12.775,-1.807 17.174,-3.687 4.254,2.399 9.463,3.687 15.459,3.687 3.088,0 6.236,-0.355 9.426,-1.023 h 67.135 l 3.354,-24.827 -5.445,-0.764 1.879,-13.356 c 0.371,-2.386 0.449,-4.66 0.449,-6.156 l -0.008,-0.375 c -0.457,-12.191 -8.139,-19.765 -20.045,-19.765 -2.404,0 -4.623,0.314 -6.676,0.852 h -34.189 l -0.035,0.244 c -2.527,-0.701 -5.41,-1.096 -8.686,-1.096 -3.801,0 -7.406,0.555 -10.76,1.598 l 0.105,-0.746 h -12.467 l 1.826,-12.951 H 613.08 l -1.846,7.658 c -1.373,5.704 -2.213,5.793 -4.453,6.03 l -4.508,0.477 c -3.049,-1.424 -6.357,-2.065 -9.602,-2.065 -2.135,0 -4.275,0.284 -6.416,0.852 h -19.291 c 0.502,-1.772 0.775,-3.674 0.775,-5.678 0,-9.601 -6.846,-16.305 -16.646,-16.305 -11.055,0 -18.775,7.721 -18.775,18.776 0,0.951 0.082,1.869 0.219,2.764 -2.135,-0.288 -4.277,-0.409 -5.553,-0.409 -2.053,0 -4.072,0.288 -6.045,0.852 h -31.342 c -2.74,-0.553 -5.641,-0.852 -8.537,-0.852 -7.138,0 -13.492,1.674 -18.808,4.723 l -3.451,-1.461 c -3.711,-1.571 -11.232,-3.262 -18.979,-3.262 -8.933,0 -16.383,2.56 -21.576,7.016 -3.265,-4.473 -8.523,-7.016 -15.228,-7.016 -4.822,0 -9.021,1.477 -12.572,3.44 -2.996,-2.204 -6.796,-3.44 -11.115,-3.44 -2.327,0 -4.48,0.315 -6.476,0.852 h -33.963 l -0.035,0.245 c -2.526,-0.702 -5.41,-1.097 -8.687,-1.097 -20.458,0 -35.307,16.031 -35.307,38.117 0,17.363 10.785,28.149 28.148,28.149 3.087,0 6.236,-0.356 9.426,-1.023 h 88.816 c 3.706,0.676 7.669,1.023 11.154,1.023 8.907,0 16.278,-2.375 21.51,-6.593 4.872,4.252 11.585,6.593 19.728,6.593 3.053,0 6.206,-0.368 9.286,-1.023 h 44.664 2.069 z"
+ id="path243"
+ inkscape:connector-curvature="0"
+ style="fill:#f5f5f5" /><g
+ id="g245"
+ transform="translate(0,16)"><g
+ id="g247"><path
+ d="m 340.308,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.095,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z"
+ id="path249"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" /><path
+ d="m 394.07,221.7 -0.171,-0.255 1.789,-10.055 2.642,-18.063 c 0.512,-3.749 0.341,-5.623 -1.96,-5.623 -2.642,0 -5.794,2.727 -9.372,5.879 l -2.727,19.512 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 -0.852,6.305 h -18.404 l -0.171,-0.341 1.875,-10.82 2.471,-17.212 c 0.512,-3.237 0.682,-5.453 -1.789,-5.453 -3.238,0 -7.413,3.664 -9.714,5.709 l -2.642,19.512 c -0.17,1.363 -0.17,1.534 1.108,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.767,-1.789 l -4.176,-1.534 0.938,-6.476 h 16.871 l -0.938,6.987 0.256,0.085 c 4.43,-3.749 9.116,-7.924 15.592,-7.924 4.687,0 7.839,2.641 8.18,7.753 l 0.256,0.086 c 4.175,-3.664 9.202,-7.839 15.252,-7.839 6.22,0 8.775,3.152 8.946,9.202 0,1.618 -0.171,3.493 -0.426,5.538 l -3.067,21.897 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.175,0.597 -0.852,6.305 H 394.07 z"
+ id="path251"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" /><path
+ d="m 443.995,190.771 -0.17,-4.431 c 0,-0.682 -0.085,-1.108 -1.022,-1.363 -1.022,-0.256 -2.642,-0.427 -4.771,-0.427 -3.579,0 -6.391,1.108 -6.391,4.09 0,2.727 2.982,3.749 6.731,5.027 6.05,2.045 13.888,4.431 13.888,13.463 0,11.076 -9.372,15.592 -20.193,15.592 -8.009,0 -14.91,-1.959 -16.273,-2.981 l 1.618,-12.355 8.691,0.512 0.255,4.941 c 0,0.597 0.171,1.108 0.938,1.363 1.278,0.427 3.238,0.768 6.05,0.768 4.687,0 7.327,-1.79 7.327,-4.687 0,-3.408 -3.152,-4.175 -8.009,-5.624 -6.135,-1.874 -12.78,-4.26 -12.78,-13.206 0,-10.48 9.116,-14.996 19.597,-14.996 6.646,0 12.866,1.533 15.081,2.471 l -1.704,12.354 -8.863,-0.511 z"
+ id="path253"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" /><path
+ d="m 489.748,218.548 c -4.175,2.386 -10.395,4.175 -16.444,4.175 -13.036,0 -18.575,-7.583 -18.575,-18.574 0,-18.83 11.588,-27.691 25.988,-27.691 6.475,0 11.843,1.874 14.229,3.578 l -1.874,13.377 -8.691,-0.426 -0.255,-5.794 c 0,-0.597 -0.086,-0.938 -0.597,-1.192 -1.022,-0.427 -2.557,-0.597 -4.175,-0.597 -5.624,0 -11.418,4.601 -11.418,17.382 0,7.839 3.493,10.395 8.436,10.395 4.346,0 8.436,-1.448 11.247,-2.556 l 2.129,7.923 z"
+ id="path255"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" /><path
+ d="m 491.364,221.7 0.853,-6.39 3.919,-0.512 c 1.193,-0.17 1.363,-0.426 1.534,-1.704 l 3.578,-25.987 c 0.086,-0.938 -0.085,-1.534 -0.852,-1.789 l -4.261,-1.534 0.938,-6.476 h 16.87 l -1.107,7.669 0.256,0.17 c 3.323,-4.771 8.095,-8.69 13.548,-8.69 1.874,0 5.112,0.341 6.561,0.767 l -2.13,15.507 -9.969,-0.341 -0.256,-4.431 c -0.086,-0.767 -0.256,-1.022 -0.938,-1.022 -1.619,0 -4.26,1.96 -6.646,4.431 l -2.981,21.643 c -0.171,1.363 -0.085,1.619 1.192,1.704 l 8.095,0.682 -0.938,6.305 h -27.266 z"
+ id="path257"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" /><path
+ d="m 536.094,221.7 -0.17,-0.426 2.045,-11.503 3.152,-22.749 c 0.17,-0.938 -0.086,-1.534 -0.853,-1.79 l -4.175,-1.448 0.852,-6.476 h 18.149 l -5.027,35.786 c -0.171,1.363 -0.085,1.534 1.192,1.704 l 4.09,0.597 -0.852,6.305 h -18.403 z m 5.879,-57.598 c 0,-5.453 3.238,-8.775 8.776,-8.775 4.175,0 6.646,2.215 6.646,6.305 0,5.368 -3.322,8.861 -8.861,8.861 -4.176,-0.001 -6.561,-2.387 -6.561,-6.391 z"
+ id="path259"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" /><path
+ d="m 556.796,239.764 -0.17,-0.341 2.471,-14.229 5.282,-38.087 c 0.171,-1.022 -0.085,-1.534 -0.767,-1.789 l -4.175,-1.534 0.938,-6.476 h 17.041 l -1.022,6.816 0.255,0.085 c 5.027,-4.686 10.311,-7.753 15.678,-7.753 7.328,0 12.44,4.686 12.44,17.041 0,11.758 -4.601,29.225 -20.449,29.225 -5.538,0 -8.605,-2.13 -11.759,-4.345 l -1.874,12.78 c -0.085,0.938 0.085,1.278 1.192,1.363 l 8.606,0.853 -0.938,6.39 h -22.749 z m 17.041,-30.247 c 2.13,1.789 4.942,3.322 8.095,3.322 6.901,0 9.458,-9.713 9.458,-17.211 0,-5.027 -1.193,-8.351 -4.431,-8.351 -3.408,0 -7.754,3.664 -10.821,6.391 l -2.301,15.849 z"
+ id="path261"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" /><path
+ d="m 635.777,219.4 c -3.749,1.789 -9.458,3.322 -14.229,3.322 -8.521,0 -12.099,-2.981 -12.099,-9.969 0,-1.107 0.085,-2.386 0.256,-3.749 l 3.066,-22.323 c 0.086,-0.512 0.086,-0.853 -0.511,-0.853 h -5.879 l 1.107,-7.839 c 7.242,-0.767 10.906,-4.431 13.122,-13.633 h 7.924 l -1.704,12.1 c -0.085,0.596 -0.085,0.852 0.597,0.852 h 11.758 l -1.193,8.521 h -12.439 l -2.812,20.364 c -0.171,1.107 -0.256,1.96 -0.256,2.727 0,2.982 1.278,4.26 4.942,4.26 2.385,0 4.771,-0.596 6.816,-1.363 l 1.534,7.583 z"
+ id="path263"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" /><path
+ d="m 671.817,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.094,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z"
+ id="path265"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" /><path
+ d="m 703.596,221.7 -0.17,-0.255 1.874,-10.396 2.471,-17.723 c 0.512,-3.578 0.341,-5.879 -2.215,-5.879 -3.664,0 -8.18,3.578 -11.077,6.135 l -2.641,19.512 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.768,-1.789 l -4.175,-1.534 0.938,-6.476 h 16.87 l -0.937,6.987 0.255,0.085 c 4.771,-4.09 9.373,-7.924 16.02,-7.924 6.475,0 9.798,3.322 10.054,10.139 0,1.363 -0.085,3.067 -0.341,4.687 l -3.067,21.812 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 L 722,221.7 h -18.404 z"
+ id="path267"
+ inkscape:connector-curvature="0"
+ style="fill:#383838" /></g><g
+ id="g269"><linearGradient
+ id="SVGID_15_"
+ gradientUnits="userSpaceOnUse"
+ x1="324.1611"
+ y1="239.7637"
+ x2="324.1611"
+ y2="155.3275"><stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop272" /><stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop274" /><a:midPointStop
+ offset="0"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" /></linearGradient><path
+ d="m 340.308,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.095,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z"
+ id="path276"
+ style="fill:url(#SVGID_15_)"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_16_"
+ gradientUnits="userSpaceOnUse"
+ x1="377.45459"
+ y1="239.7637"
+ x2="377.45459"
+ y2="155.3277"><stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop279" /><stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop281" /><a:midPointStop
+ offset="0"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" /></linearGradient><path
+ d="m 394.07,221.7 -0.171,-0.255 1.789,-10.055 2.642,-18.063 c 0.512,-3.749 0.341,-5.623 -1.96,-5.623 -2.642,0 -5.794,2.727 -9.372,5.879 l -2.727,19.512 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 -0.852,6.305 h -18.404 l -0.171,-0.341 1.875,-10.82 2.471,-17.212 c 0.512,-3.237 0.682,-5.453 -1.789,-5.453 -3.238,0 -7.413,3.664 -9.714,5.709 l -2.642,19.512 c -0.17,1.363 -0.17,1.534 1.108,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.767,-1.789 l -4.176,-1.534 0.938,-6.476 h 16.871 l -0.938,6.987 0.256,0.085 c 4.43,-3.749 9.116,-7.924 15.592,-7.924 4.687,0 7.839,2.641 8.18,7.753 l 0.256,0.086 c 4.175,-3.664 9.202,-7.839 15.252,-7.839 6.22,0 8.775,3.152 8.946,9.202 0,1.618 -0.171,3.493 -0.426,5.538 l -3.067,21.897 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.175,0.597 -0.852,6.305 H 394.07 z"
+ id="path283"
+ style="fill:url(#SVGID_16_)"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_17_"
+ gradientUnits="userSpaceOnUse"
+ x1="435.17719"
+ y1="239.7637"
+ x2="435.17719"
+ y2="155.3275"><stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop286" /><stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop288" /><a:midPointStop
+ offset="0"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" /></linearGradient><path
+ d="m 443.995,190.771 -0.17,-4.431 c 0,-0.682 -0.085,-1.108 -1.022,-1.363 -1.022,-0.256 -2.642,-0.427 -4.771,-0.427 -3.579,0 -6.391,1.108 -6.391,4.09 0,2.727 2.982,3.749 6.731,5.027 6.05,2.045 13.888,4.431 13.888,13.463 0,11.076 -9.372,15.592 -20.193,15.592 -8.009,0 -14.91,-1.959 -16.273,-2.981 l 1.618,-12.355 8.691,0.512 0.255,4.941 c 0,0.597 0.171,1.108 0.938,1.363 1.278,0.427 3.238,0.768 6.05,0.768 4.687,0 7.327,-1.79 7.327,-4.687 0,-3.408 -3.152,-4.175 -8.009,-5.624 -6.135,-1.874 -12.78,-4.26 -12.78,-13.206 0,-10.48 9.116,-14.996 19.597,-14.996 6.646,0 12.866,1.533 15.081,2.471 l -1.704,12.354 -8.863,-0.511 z"
+ id="path290"
+ style="fill:url(#SVGID_17_)"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_18_"
+ gradientUnits="userSpaceOnUse"
+ x1="474.83691"
+ y1="239.7637"
+ x2="474.83691"
+ y2="155.3275"><stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop293" /><stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop295" /><a:midPointStop
+ offset="0"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" /></linearGradient><path
+ d="m 489.748,218.548 c -4.175,2.386 -10.395,4.175 -16.444,4.175 -13.036,0 -18.575,-7.583 -18.575,-18.574 0,-18.83 11.588,-27.691 25.988,-27.691 6.475,0 11.843,1.874 14.229,3.578 l -1.874,13.377 -8.691,-0.426 -0.255,-5.794 c 0,-0.597 -0.086,-0.938 -0.597,-1.192 -1.022,-0.427 -2.557,-0.597 -4.175,-0.597 -5.624,0 -11.418,4.601 -11.418,17.382 0,7.839 3.493,10.395 8.436,10.395 4.346,0 8.436,-1.448 11.247,-2.556 l 2.129,7.923 z"
+ id="path297"
+ style="fill:url(#SVGID_18_)"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_19_"
+ gradientUnits="userSpaceOnUse"
+ x1="512.28223"
+ y1="239.7637"
+ x2="512.28223"
+ y2="155.3277"><stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop300" /><stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop302" /><a:midPointStop
+ offset="0"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" /></linearGradient><path
+ d="m 491.364,221.7 0.853,-6.39 3.919,-0.512 c 1.193,-0.17 1.363,-0.426 1.534,-1.704 l 3.578,-25.987 c 0.086,-0.938 -0.085,-1.534 -0.852,-1.789 l -4.261,-1.534 0.938,-6.476 h 16.87 l -1.107,7.669 0.256,0.17 c 3.323,-4.771 8.095,-8.69 13.548,-8.69 1.874,0 5.112,0.341 6.561,0.767 l -2.13,15.507 -9.969,-0.341 -0.256,-4.431 c -0.086,-0.767 -0.256,-1.022 -0.938,-1.022 -1.619,0 -4.26,1.96 -6.646,4.431 l -2.981,21.643 c -0.171,1.363 -0.085,1.619 1.192,1.704 l 8.095,0.682 -0.938,6.305 h -27.266 z"
+ id="path304"
+ style="fill:url(#SVGID_19_)"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_20_"
+ gradientUnits="userSpaceOnUse"
+ x1="546.65918"
+ y1="239.7637"
+ x2="546.65918"
+ y2="155.32719"><stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop307" /><stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop309" /><a:midPointStop
+ offset="0"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" /></linearGradient><path
+ d="m 536.094,221.7 -0.17,-0.426 2.045,-11.503 3.152,-22.749 c 0.17,-0.938 -0.086,-1.534 -0.853,-1.79 l -4.175,-1.448 0.852,-6.476 h 18.149 l -5.027,35.786 c -0.171,1.363 -0.085,1.534 1.192,1.704 l 4.09,0.597 -0.852,6.305 h -18.403 z m 5.879,-57.598 c 0,-5.453 3.238,-8.775 8.776,-8.775 4.175,0 6.646,2.215 6.646,6.305 0,5.368 -3.322,8.861 -8.861,8.861 -4.176,-0.001 -6.561,-2.387 -6.561,-6.391 z"
+ id="path311"
+ style="fill:url(#SVGID_20_)"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_21_"
+ gradientUnits="userSpaceOnUse"
+ x1="580.69629"
+ y1="239.7637"
+ x2="580.69629"
+ y2="155.32719"><stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop314" /><stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop316" /><a:midPointStop
+ offset="0"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" /></linearGradient><path
+ d="m 556.796,239.764 -0.17,-0.341 2.471,-14.229 5.282,-38.087 c 0.171,-1.022 -0.085,-1.534 -0.767,-1.789 l -4.175,-1.534 0.938,-6.476 h 17.041 l -1.022,6.816 0.255,0.085 c 5.027,-4.686 10.311,-7.753 15.678,-7.753 7.328,0 12.44,4.686 12.44,17.041 0,11.758 -4.601,29.225 -20.449,29.225 -5.538,0 -8.605,-2.13 -11.759,-4.345 l -1.874,12.78 c -0.085,0.938 0.085,1.278 1.192,1.363 l 8.606,0.853 -0.938,6.39 h -22.749 z m 17.041,-30.247 c 2.13,1.789 4.942,3.322 8.095,3.322 6.901,0 9.458,-9.713 9.458,-17.211 0,-5.027 -1.193,-8.351 -4.431,-8.351 -3.408,0 -7.754,3.664 -10.821,6.391 l -2.301,15.849 z"
+ id="path318"
+ style="fill:url(#SVGID_21_)"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_22_"
+ gradientUnits="userSpaceOnUse"
+ x1="622.7832"
+ y1="239.7637"
+ x2="622.7832"
+ y2="155.3268"><stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop321" /><stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop323" /><a:midPointStop
+ offset="0"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" /></linearGradient><path
+ d="m 635.777,219.4 c -3.749,1.789 -9.458,3.322 -14.229,3.322 -8.521,0 -12.099,-2.981 -12.099,-9.969 0,-1.107 0.085,-2.386 0.256,-3.749 l 3.066,-22.323 c 0.086,-0.512 0.086,-0.853 -0.511,-0.853 h -5.879 l 1.107,-7.839 c 7.242,-0.767 10.906,-4.431 13.122,-13.633 h 7.924 l -1.704,12.1 c -0.085,0.596 -0.085,0.852 0.597,0.852 h 11.758 l -1.193,8.521 h -12.439 l -2.812,20.364 c -0.171,1.107 -0.256,1.96 -0.256,2.727 0,2.982 1.278,4.26 4.942,4.26 2.385,0 4.771,-0.596 6.816,-1.363 l 1.534,7.583 z"
+ id="path325"
+ style="fill:url(#SVGID_22_)"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_23_"
+ gradientUnits="userSpaceOnUse"
+ x1="655.6709"
+ y1="239.7637"
+ x2="655.6709"
+ y2="155.3275"><stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop328" /><stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop330" /><a:midPointStop
+ offset="0"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" /></linearGradient><path
+ d="m 671.817,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.094,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z"
+ id="path332"
+ style="fill:url(#SVGID_23_)"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_24_"
+ gradientUnits="userSpaceOnUse"
+ x1="697.92969"
+ y1="239.7637"
+ x2="697.92969"
+ y2="155.3277"><stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop335" /><stop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0"
+ id="stop337" /><a:midPointStop
+ offset="0"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="0.6933"
+ style="stop-color:#000000" /><a:midPointStop
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0" /></linearGradient><path
+ d="m 703.596,221.7 -0.17,-0.255 1.874,-10.396 2.471,-17.723 c 0.512,-3.578 0.341,-5.879 -2.215,-5.879 -3.664,0 -8.18,3.578 -11.077,6.135 l -2.641,19.512 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.768,-1.789 l -4.175,-1.534 0.938,-6.476 h 16.87 l -0.937,6.987 0.255,0.085 c 4.771,-4.09 9.373,-7.924 16.02,-7.924 6.475,0 9.798,3.322 10.054,10.139 0,1.363 -0.085,3.067 -0.341,4.687 l -3.067,21.812 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 L 722,221.7 h -18.404 z"
+ id="path339"
+ style="fill:url(#SVGID_24_)"
+ inkscape:connector-curvature="0" /></g></g><g
+ id="g4141"
+ transform="matrix(0.81856441,0,0,0.81856441,79.234731,-94.128741)"><g
+ id="g4143"></g><g
+ id="g4165"><linearGradient
+ y2="155.3275"
+ x2="324.1611"
+ y1="239.7637"
+ x1="324.1611"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4167"><stop
+ id="stop4169"
+ style="stop-color:#000000"
+ offset="0" /><stop
+ id="stop4171"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" /><a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /></linearGradient><linearGradient
+ y2="155.3277"
+ x2="377.45459"
+ y1="239.7637"
+ x1="377.45459"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4175"><stop
+ id="stop4177"
+ style="stop-color:#000000"
+ offset="0" /><stop
+ id="stop4179"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" /><a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /></linearGradient><linearGradient
+ y2="155.3275"
+ x2="435.17719"
+ y1="239.7637"
+ x1="435.17719"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4183"><stop
+ id="stop4185"
+ style="stop-color:#000000"
+ offset="0" /><stop
+ id="stop4187"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" /><a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /></linearGradient><linearGradient
+ y2="155.3275"
+ x2="474.83691"
+ y1="239.7637"
+ x1="474.83691"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4191"><stop
+ id="stop4193"
+ style="stop-color:#000000"
+ offset="0" /><stop
+ id="stop4195"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" /><a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /></linearGradient><linearGradient
+ y2="155.3277"
+ x2="512.28223"
+ y1="239.7637"
+ x1="512.28223"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4199"><stop
+ id="stop4201"
+ style="stop-color:#000000"
+ offset="0" /><stop
+ id="stop4203"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" /><a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /></linearGradient><linearGradient
+ y2="155.32719"
+ x2="546.65918"
+ y1="239.7637"
+ x1="546.65918"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4207"><stop
+ id="stop4209"
+ style="stop-color:#000000"
+ offset="0" /><stop
+ id="stop4211"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" /><a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /></linearGradient><linearGradient
+ y2="155.32719"
+ x2="580.69629"
+ y1="239.7637"
+ x1="580.69629"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4215"><stop
+ id="stop4217"
+ style="stop-color:#000000"
+ offset="0" /><stop
+ id="stop4219"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" /><a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /></linearGradient><linearGradient
+ y2="155.3268"
+ x2="622.7832"
+ y1="239.7637"
+ x1="622.7832"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4223"><stop
+ id="stop4225"
+ style="stop-color:#000000"
+ offset="0" /><stop
+ id="stop4227"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" /><a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /></linearGradient><linearGradient
+ y2="155.3275"
+ x2="655.6709"
+ y1="239.7637"
+ x1="655.6709"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4231"><stop
+ id="stop4233"
+ style="stop-color:#000000"
+ offset="0" /><stop
+ id="stop4235"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" /><a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /></linearGradient><linearGradient
+ y2="155.3277"
+ x2="697.92969"
+ y1="239.7637"
+ x1="697.92969"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4239"><stop
+ id="stop4241"
+ style="stop-color:#000000"
+ offset="0" /><stop
+ id="stop4243"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0" /><a:midPointStop
+ style="stop-color:#000000"
+ offset="0.6933" /><a:midPointStop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1" /></linearGradient></g></g></svg>
+ </a>
+
+ <div class="spinner" id='spinner'></div>
+ <div class="emscripten" id="status">Downloading...</div>
+
+<span id='controls'>
+ <span><input type="checkbox" id="resize">Resize canvas</span>
+ <span><input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer &nbsp;&nbsp;&nbsp;</span>
+ <span><input type="button" value="Fullscreen" onclick="Module.requestFullScreen(document.getElementById('pointerLock').checked,
+ document.getElementById('resize').checked)">
+ </span>
+</span>
+
+ <div class="emscripten">
+ <progress value="0" max="100" id="progress" hidden=1></progress>
+ </div>
+
+
+ <div class="emscripten_border">
+ <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
+ </div>
+ <textarea id="output" rows="8"></textarea>
+
+ <script type='text/javascript'>
+ var statusElement = document.getElementById('status');
+ var progressElement = document.getElementById('progress');
+ var spinnerElement = document.getElementById('spinner');
+
+ var Module = {
+ TOTAL_MEMORY: $GODOTTMEM,
+ preRun: [],
+ postRun: [],
+ print: (function() {
+ var element = document.getElementById('output');
+ if (element) element.value = ''; // clear browser cache
+ return function(text) {
+ if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
+ // These replacements are necessary if you render to raw HTML
+ //text = text.replace(/&/g, "&amp;");
+ //text = text.replace(/</g, "&lt;");
+ //text = text.replace(/>/g, "&gt;");
+ //text = text.replace('\n', '<br>', 'g');
+ console.log(text);
+ if (element) {
+ element.value += text + "\n";
+ element.scrollTop = element.scrollHeight; // focus on bottom
+ }
+ };
+ })(),
+ printErr: function(text) {
+ if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
+ if (0) { // XXX disabled for safety typeof dump == 'function') {
+ dump(text + '\n'); // fast, straight to the real console
+ } else {
+ console.error(text);
+ }
+ },
+ canvas: (function() {
+ var canvas = document.getElementById('canvas');
+
+ // As a default initial behavior, pop up an alert when webgl context is lost. To make your
+ // application robust, you may want to override this behavior before shipping!
+ // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
+ canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
+
+ return canvas;
+ })(),
+ setStatus: function(text) {
+ if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
+ if (text === Module.setStatus.text) return;
+ var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
+ var now = Date.now();
+ if (m && now - Date.now() < 30) return; // if this is a progress update, skip it if too soon
+ if (m) {
+ text = m[1];
+ progressElement.value = parseInt(m[2])*100;
+ progressElement.max = parseInt(m[4])*100;
+ progressElement.hidden = false;
+ spinnerElement.hidden = false;
+ } else {
+ progressElement.value = null;
+ progressElement.max = null;
+ progressElement.hidden = true;
+ if (!text) spinnerElement.style.display = 'none';
+ }
+ statusElement.innerHTML = text;
+ },
+ totalDependencies: 0,
+ monitorRunDependencies: function(left) {
+ this.totalDependencies = Math.max(this.totalDependencies, left);
+ Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
+ }
+ };
+ Module.setStatus('Downloading...');
+ window.onerror = function(event) {
+ // TODO: do not warn on ok events like simulating an infinite loop or exitStatus
+ Module.setStatus('Exception thrown, see JavaScript console');
+ spinnerElement.style.display = 'none';
+ Module.setStatus = function(text) {
+ if (text) Module.printErr('[post-exception status] ' + text);
+ };
+ };
+ </script>
+ <script type="text/javascript" src="$GODOTFS"></script>
+ <script>
+
+ (function() {
+ var memoryInitializer = '$GODOTMEM';
+ if (typeof Module['locateFile'] === 'function') {
+ memoryInitializer = Module['locateFile'](memoryInitializer);
+ } else if (Module['memoryInitializerPrefixURL']) {
+ memoryInitializer = Module['memoryInitializerPrefixURL'] + memoryInitializer;
+ }
+ var xhr = Module['memoryInitializerRequest'] = new XMLHttpRequest();
+ xhr.open('GET', memoryInitializer, true);
+ xhr.responseType = 'arraybuffer';
+ xhr.send(null);
+ })();
+
+ var script = document.createElement('script');
+ script.src = "$GODOTJS";
+ document.body.appendChild(script);
+
+</script>
+ </body>
+</html>
diff --git a/tools/html_fs/filesystem.js b/tools/html_fs/godotfs.js
index 93cc30556b..93cc30556b 100644
--- a/tools/html_fs/filesystem.js
+++ b/tools/html_fs/godotfs.js
diff --git a/tools/osx_template.app/Contents/Resources/icon.icns b/tools/osx_template.app/Contents/Resources/icon.icns
index 18bc68d6ea..4a3dc0415a 100644
--- a/tools/osx_template.app/Contents/Resources/icon.icns
+++ b/tools/osx_template.app/Contents/Resources/icon.icns
Binary files differ
diff --git a/tools/pck/SCsub b/tools/pck/SCsub
index b1fed9a472..cf98ae145d 100644
--- a/tools/pck/SCsub
+++ b/tools/pck/SCsub
@@ -2,4 +2,3 @@ Import('env')
if env["tools"] == "yes":
env.add_source_files(env.tool_sources, "*.cpp")
-
diff --git a/tools/pck/pck_packer.cpp b/tools/pck/pck_packer.cpp
index 09611b3a93..d398fefb5f 100644
--- a/tools/pck/pck_packer.cpp
+++ b/tools/pck/pck_packer.cpp
@@ -136,7 +136,7 @@ Error PCKPacker::flush(bool p_verbose) {
count += 1;
if (p_verbose) {
if (count % 100 == 0) {
- printf("%i/%i (%.2f\%)\r", count, files.size(), float(count) / files.size() * 100);
+ printf("%i/%i (%.2f)\r", count, files.size(), float(count) / files.size() * 100);
fflush(stdout);
};
};
diff --git a/tools/pck/pck_packer.h b/tools/pck/pck_packer.h
index 76752a6170..2bb51128e9 100644
--- a/tools/pck/pck_packer.h
+++ b/tools/pck/pck_packer.h
@@ -1,10 +1,10 @@
-#include "core/object.h"
+#include "core/reference.h"
class FileAccess;
-class PCKPacker : public Object {
+class PCKPacker : public Reference {
- OBJ_TYPE(PCKPacker, Object);
+ OBJ_TYPE(PCKPacker, Reference);
FileAccess* file;
int alignment;
diff --git a/tools/pe_bliss/README b/tools/pe_bliss/README
new file mode 100644
index 0000000000..d5d1355444
--- /dev/null
+++ b/tools/pe_bliss/README
@@ -0,0 +1,84 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+
+Открытая бесплатная библиотека для работы с PE-файлами PE Bliss.
+Бесплатна к использованию, модификации и распространению.
+Автор: DX
+(c) DX 2011-2012, kaimi.ru
+
+Совместимость: Windows, Linux
+
+Возможности:
+[+] Создание PE или PE+ файла с нуля
+[+] Чтение 32-разрядных и 64-разрядных PE-файлов (PE, PE+) и единообразная работа с ними
+[+] Пересборка 32-разрядных и 64-разрядных PE-файлов
+[+] Работа с директориями и заголовками
+[+] Конвертирование адресов
+[+] Чтение и редактирование секций PE-файла
+[+] Чтение и редактирование таблицы импортов
+[+] Чтение и редактирование таблицы экспортов
+[+] Чтение и редактирование таблиц релокаций
+[+] Чтение и редактирование ресурсов
+[+] Чтение и редактирование TLS
+[+] Чтение и редактирование конфигурации образа (image config)
+[+] Чтение базовой информации .NET
+[+] Чтение и редактирование информации о привязанном импорте
+[+] Чтение директории исключений (только PE+)
+[+] Чтение отладочной директории с расширенной информацией
+[+] Вычисление энтропии
+[+] Изменение файлового выравнивания
+[+] Изменение базового адреса загрузки
+[+] Работа с DOS Stub'ом и Rich overlay
+[+] Высокоуровневое чтение ресурсов: картинки, иконки, курсоры, информация о версии, строковые таблицы, таблицы сообщений
+[+] Высокоуровневое редактирование ресурсов: картинки, иконки, курсоры, информация о версии
+
+[English]
+Open a free library for working with PE-file PE Bliss.
+Free to use, modify, and distribute.
+Author: DX
+(c) DX 2011-2012, kaimi.ru
+Compatibility: Windows, Linux
+
+### Capabilities:
+[+] Creation of PE or PE + file from scratch
+[+] Reading the 32-bit and 64-bit PE-file (PE, PE +) and uniform working with them
+[+] Rebuild 32-bit and 64-bit PE-files
+[+] Working with the directors and titles
+[+] Converting addresses
+[+] Reading and editing sections of PE-file
+[+] Reading and editing the import table
+[+] Reading and editing tables exports
+[+] Reading and editing tables relocations
+[+] Reading and editing resources
+[+] Reading and editing TLS
+[+] Reading and editing the configuration of the image (image config)
+[+] Reading data base .NET
+[+] Reading and editing information about tethered import
+[+] Read the directory exceptions (only PE +)
+[+] Read debug directories with extended information
+[+] The calculation of entropy
+[+] Changing file alignment
+[+] Change the base load address
+[+] Support of DOS Stub'om and Rich overlay
+[+] High-level reading resources: images, icons, cursors, version information, string tables, message table
+[+] High-level editing resources: images, icons, cursors, version information \ No newline at end of file
diff --git a/tools/pe_bliss/SCsub b/tools/pe_bliss/SCsub
new file mode 100644
index 0000000000..34524f10ef
--- /dev/null
+++ b/tools/pe_bliss/SCsub
@@ -0,0 +1,5 @@
+Import('env')
+
+env.add_source_files(env.tool_sources,"*.cpp")
+
+Export('env')
diff --git a/tools/pe_bliss/entropy.cpp b/tools/pe_bliss/entropy.cpp
new file mode 100644
index 0000000000..acefa63e83
--- /dev/null
+++ b/tools/pe_bliss/entropy.cpp
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <cmath>
+#include "entropy.h"
+#include "utils.h"
+
+namespace pe_bliss
+{
+//Calculates entropy for PE image section
+double entropy_calculator::calculate_entropy(const section& s)
+{
+ if(s.get_raw_data().empty()) //Don't count entropy for empty sections
+ throw pe_exception("Section is empty", pe_exception::section_is_empty);
+
+ return calculate_entropy(s.get_raw_data().data(), s.get_raw_data().length());
+}
+
+//Calculates entropy for istream (from current position of stream)
+double entropy_calculator::calculate_entropy(std::istream& file)
+{
+ uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes
+
+ if(file.bad())
+ throw pe_exception("Stream is bad", pe_exception::stream_is_bad);
+
+ std::streamoff pos = file.tellg();
+
+ std::streamoff length = pe_utils::get_file_size(file);
+ length -= file.tellg();
+
+ if(!length) //Don't calculate entropy for empty buffers
+ throw pe_exception("Data length is zero", pe_exception::data_is_empty);
+
+ //Count bytes
+ for(std::streamoff i = 0; i != length; ++i)
+ ++byte_count[static_cast<unsigned char>(file.get())];
+
+ file.seekg(pos);
+
+ return calculate_entropy(byte_count, length);
+}
+
+//Calculates entropy for data block
+double entropy_calculator::calculate_entropy(const char* data, size_t length)
+{
+ uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes
+
+ if(!length) //Don't calculate entropy for empty buffers
+ throw pe_exception("Data length is zero", pe_exception::data_is_empty);
+
+ //Count bytes
+ for(size_t i = 0; i != length; ++i)
+ ++byte_count[static_cast<unsigned char>(data[i])];
+
+ return calculate_entropy(byte_count, length);
+}
+
+//Calculates entropy for this PE file (only section data)
+double entropy_calculator::calculate_entropy(const pe_base& pe)
+{
+ uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes
+
+ size_t total_data_length = 0;
+
+ //Count bytes for each section
+ for(section_list::const_iterator it = pe.get_image_sections().begin(); it != pe.get_image_sections().end(); ++it)
+ {
+ const std::string& data = (*it).get_raw_data();
+ size_t length = data.length();
+ total_data_length += length;
+ for(size_t i = 0; i != length; ++i)
+ ++byte_count[static_cast<unsigned char>(data[i])];
+ }
+
+ return calculate_entropy(byte_count, total_data_length);
+}
+
+//Calculates entropy from bytes count
+double entropy_calculator::calculate_entropy(const uint32_t byte_count[256], std::streamoff total_length)
+{
+ double entropy = 0.; //Entropy result value
+ //Calculate entropy
+ for(uint32_t i = 0; i < 256; ++i)
+ {
+ double temp = static_cast<double>(byte_count[i]) / total_length;
+ if(temp > 0.)
+ entropy += std::abs(temp * (std::log(temp) * pe_utils::log_2));
+ }
+
+ return entropy;
+}
+}
diff --git a/tools/pe_bliss/entropy.h b/tools/pe_bliss/entropy.h
new file mode 100644
index 0000000000..7d225a3e32
--- /dev/null
+++ b/tools/pe_bliss/entropy.h
@@ -0,0 +1,51 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <istream>
+#include "pe_base.h"
+
+namespace pe_bliss
+{
+class entropy_calculator
+{
+public:
+ //Calculates entropy for PE image section
+ static double calculate_entropy(const section& s);
+
+ //Calculates entropy for istream (from current position of stream)
+ static double calculate_entropy(std::istream& file);
+
+ //Calculates entropy for data block
+ static double calculate_entropy(const char* data, size_t length);
+
+ //Calculates entropy for this PE file (only section data)
+ static double calculate_entropy(const pe_base& pe);
+
+private:
+ entropy_calculator();
+ entropy_calculator(const entropy_calculator&);
+ entropy_calculator& operator=(const entropy_calculator&);
+
+ //Calculates entropy from bytes count
+ static double calculate_entropy(const uint32_t byte_count[256], std::streamoff total_length);
+};
+}
diff --git a/tools/pe_bliss/file_version_info.cpp b/tools/pe_bliss/file_version_info.cpp
new file mode 100644
index 0000000000..3f2ba454b4
--- /dev/null
+++ b/tools/pe_bliss/file_version_info.cpp
@@ -0,0 +1,440 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "file_version_info.h"
+#include "pe_structures.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//Default constructor
+file_version_info::file_version_info()
+ :file_version_ms_(0), file_version_ls_(0),
+ product_version_ms_(0), product_version_ls_(0),
+ file_flags_(0),
+ file_os_(0),
+ file_type_(0), file_subtype_(0),
+ file_date_ms_(0), file_date_ls_(0)
+{}
+
+//Constructor from Windows fixed version info structure
+file_version_info::file_version_info(const vs_fixedfileinfo& info)
+ :file_version_ms_(info.dwFileVersionMS), file_version_ls_(info.dwFileVersionLS),
+ product_version_ms_(info.dwProductVersionMS), product_version_ls_(info.dwProductVersionLS),
+ file_flags_(info.dwFileFlags),
+ file_os_(info.dwFileOS),
+ file_type_(info.dwFileType), file_subtype_(info.dwFileSubtype),
+ file_date_ms_(info.dwFileDateMS), file_date_ls_(info.dwFileDateLS)
+{}
+
+//Returns true if file is debug-built
+bool file_version_info::is_debug() const
+{
+ return file_flags_ & vs_ff_debug ? true : false;
+}
+
+//Returns true if file is release-built
+bool file_version_info::is_prerelease() const
+{
+ return file_flags_ & vs_ff_prerelease ? true : false;
+}
+
+//Returns true if file is patched
+bool file_version_info::is_patched() const
+{
+ return file_flags_ & vs_ff_patched ? true : false;
+}
+
+//Returns true if private build
+bool file_version_info::is_private_build() const
+{
+ return file_flags_ & vs_ff_privatebuild ? true : false;
+}
+
+//Returns true if special build
+bool file_version_info::is_special_build() const
+{
+ return file_flags_ & vs_ff_specialbuild ? true : false;
+}
+
+//Returns true if info inferred
+bool file_version_info::is_info_inferred() const
+{
+ return file_flags_ & vs_ff_infoinferred ? true : false;
+}
+
+//Retuens file flags (raw DWORD)
+uint32_t file_version_info::get_file_flags() const
+{
+ return file_flags_;
+}
+
+//Returns file version most significant DWORD
+uint32_t file_version_info::get_file_version_ms() const
+{
+ return file_version_ms_;
+}
+
+//Returns file version least significant DWORD
+uint32_t file_version_info::get_file_version_ls() const
+{
+ return file_version_ls_;
+}
+
+//Returns product version most significant DWORD
+uint32_t file_version_info::get_product_version_ms() const
+{
+ return product_version_ms_;
+}
+
+//Returns product version least significant DWORD
+uint32_t file_version_info::get_product_version_ls() const
+{
+ return product_version_ls_;
+}
+
+//Returns file OS type (raw DWORD)
+uint32_t file_version_info::get_file_os_raw() const
+{
+ return file_os_;
+}
+
+//Returns file OS type
+file_version_info::file_os_type file_version_info::get_file_os() const
+{
+ //Determine file operation system type
+ switch(file_os_)
+ {
+ case vos_dos:
+ return file_os_dos;
+
+ case vos_os216:
+ return file_os_os216;
+
+ case vos_os232:
+ return file_os_os232;
+
+ case vos_nt:
+ return file_os_nt;
+
+ case vos_wince:
+ return file_os_wince;
+
+ case vos__windows16:
+ return file_os_win16;
+
+ case vos__pm16:
+ return file_os_pm16;
+
+ case vos__pm32:
+ return file_os_pm32;
+
+ case vos__windows32:
+ return file_os_win32;
+
+ case vos_dos_windows16:
+ return file_os_dos_win16;
+
+ case vos_dos_windows32:
+ return file_os_dos_win32;
+
+ case vos_os216_pm16:
+ return file_os_os216_pm16;
+
+ case vos_os232_pm32:
+ return file_os_os232_pm32;
+
+ case vos_nt_windows32:
+ return file_os_nt_win32;
+ }
+
+ return file_os_unknown;
+}
+
+//Returns file type (raw DWORD)
+uint32_t file_version_info::get_file_type_raw() const
+{
+ return file_type_;
+}
+
+//Returns file type
+file_version_info::file_type file_version_info::get_file_type() const
+{
+ //Determine file type
+ switch(file_type_)
+ {
+ case vft_app:
+ return file_type_application;
+
+ case vft_dll:
+ return file_type_dll;
+
+ case vft_drv:
+ return file_type_driver;
+
+ case vft_font:
+ return file_type_font;
+
+ case vft_vxd:
+ return file_type_vxd;
+
+ case vft_static_lib:
+ return file_type_static_lib;
+ }
+
+ return file_type_unknown;
+}
+
+//Returns file subtype (usually non-zero for drivers and fonts)
+uint32_t file_version_info::get_file_subtype() const
+{
+ return file_subtype_;
+}
+
+//Returns file date most significant DWORD
+uint32_t file_version_info::get_file_date_ms() const
+{
+ return file_date_ms_;
+}
+
+//Returns file date least significant DWORD
+uint32_t file_version_info::get_file_date_ls() const
+{
+ return file_date_ls_;
+}
+
+//Helper to set file flag
+void file_version_info::set_file_flag(uint32_t flag)
+{
+ file_flags_ |= flag;
+}
+
+//Helper to clear file flag
+void file_version_info::clear_file_flag(uint32_t flag)
+{
+ file_flags_ &= ~flag;
+}
+
+//Helper to set or clear file flag
+void file_version_info::set_file_flag(uint32_t flag, bool set_flag)
+{
+ set_flag ? set_file_flag(flag) : clear_file_flag(flag);
+}
+
+//Sets if file is debug-built
+void file_version_info::set_debug(bool debug)
+{
+ set_file_flag(vs_ff_debug, debug);
+}
+
+//Sets if file is prerelease
+void file_version_info::set_prerelease(bool prerelease)
+{
+ set_file_flag(vs_ff_prerelease, prerelease);
+}
+
+//Sets if file is patched
+void file_version_info::set_patched(bool patched)
+{
+ set_file_flag(vs_ff_patched, patched);
+}
+
+//Sets if private build
+void file_version_info::set_private_build(bool private_build)
+{
+ set_file_flag(vs_ff_privatebuild, private_build);
+}
+
+//Sets if special build
+void file_version_info::set_special_build(bool special_build)
+{
+ set_file_flag(vs_ff_specialbuild, special_build);
+}
+
+//Sets if info inferred
+void file_version_info::set_info_inferred(bool info_inferred)
+{
+ set_file_flag(vs_ff_infoinferred, info_inferred);
+}
+
+//Sets flags (raw DWORD)
+void file_version_info::set_file_flags(uint32_t file_flags)
+{
+ file_flags_ = file_flags;
+}
+
+//Sets file version most significant DWORD
+void file_version_info::set_file_version_ms(uint32_t file_version_ms)
+{
+ file_version_ms_ = file_version_ms;
+}
+
+//Sets file version least significant DWORD
+void file_version_info::set_file_version_ls(uint32_t file_version_ls)
+{
+ file_version_ls_ = file_version_ls;
+}
+
+//Sets product version most significant DWORD
+void file_version_info::set_product_version_ms(uint32_t product_version_ms)
+{
+ product_version_ms_ = product_version_ms;
+}
+
+//Sets product version least significant DWORD
+void file_version_info::set_product_version_ls(uint32_t product_version_ls)
+{
+ product_version_ls_ = product_version_ls;
+}
+
+//Sets file OS type (raw DWORD)
+void file_version_info::set_file_os_raw(uint32_t file_os)
+{
+ file_os_ = file_os;
+}
+
+//Sets file OS type
+void file_version_info::set_file_os(file_os_type file_os)
+{
+ //Determine file operation system type
+ switch(file_os)
+ {
+ case file_os_dos:
+ file_os_ = vos_dos;
+ return;
+
+ case file_os_os216:
+ file_os_ = vos_os216;
+ return;
+
+ case file_os_os232:
+ file_os_ = vos_os232;
+ return;
+
+ case file_os_nt:
+ file_os_ = vos_nt;
+ return;
+
+ case file_os_wince:
+ file_os_ = vos_wince;
+ return;
+
+ case file_os_win16:
+ file_os_ = vos__windows16;
+ return;
+
+ case file_os_pm16:
+ file_os_ = vos__pm16;
+ return;
+
+ case file_os_pm32:
+ file_os_ = vos__pm32;
+ return;
+
+ case file_os_win32:
+ file_os_ = vos__windows32;
+ return;
+
+ case file_os_dos_win16:
+ file_os_ = vos_dos_windows16;
+ return;
+
+ case file_os_dos_win32:
+ file_os_ = vos_dos_windows32;
+ return;
+
+ case file_os_os216_pm16:
+ file_os_ = vos_os216_pm16;
+ return;
+
+ case file_os_os232_pm32:
+ file_os_ = vos_os232_pm32;
+ return;
+
+ case file_os_nt_win32:
+ file_os_ = vos_nt_windows32;
+ return;
+
+ default:
+ return;
+ }
+}
+
+//Sets file type (raw DWORD)
+void file_version_info::set_file_type_raw(uint32_t file_type)
+{
+ file_type_ = file_type;
+}
+
+//Sets file type
+void file_version_info::set_file_type(file_type file_type)
+{
+ //Determine file type
+ switch(file_type)
+ {
+ case file_type_application:
+ file_type_ = vft_app;
+ return;
+
+ case file_type_dll:
+ file_type_ = vft_dll;
+ return;
+
+ case file_type_driver:
+ file_type_ = vft_drv;
+ return;
+
+ case file_type_font:
+ file_type_ = vft_font;
+ return;
+
+ case file_type_vxd:
+ file_type_ = vft_vxd;
+ return;
+
+ case file_type_static_lib:
+ file_type_ = vft_static_lib;
+ return;
+
+ default:
+ return;
+ }
+}
+
+//Sets file subtype (usually non-zero for drivers and fonts)
+void file_version_info::set_file_subtype(uint32_t file_subtype)
+{
+ file_subtype_ = file_subtype;
+}
+
+//Sets file date most significant DWORD
+void file_version_info::set_file_date_ms(uint32_t file_date_ms)
+{
+ file_date_ms_ = file_date_ms;
+}
+
+//Sets file date least significant DWORD
+void file_version_info::set_file_date_ls(uint32_t file_date_ls)
+{
+ file_date_ls_ = file_date_ls;
+}
+}
diff --git a/tools/pe_bliss/file_version_info.h b/tools/pe_bliss/file_version_info.h
new file mode 100644
index 0000000000..d898351ba1
--- /dev/null
+++ b/tools/pe_bliss/file_version_info.h
@@ -0,0 +1,199 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <string>
+#include <map>
+#include "stdint_defs.h"
+#include "pe_structures.h"
+
+namespace pe_bliss
+{
+//Structure representing fixed file version info
+class file_version_info
+{
+public:
+ //Enumeration of file operating system types
+ enum file_os_type
+ {
+ file_os_unknown,
+ file_os_dos,
+ file_os_os216,
+ file_os_os232,
+ file_os_nt,
+ file_os_wince,
+ file_os_win16,
+ file_os_pm16,
+ file_os_pm32,
+ file_os_win32,
+ file_os_dos_win16,
+ file_os_dos_win32,
+ file_os_os216_pm16,
+ file_os_os232_pm32,
+ file_os_nt_win32
+ };
+
+ //Enumeration of file types
+ enum file_type
+ {
+ file_type_unknown,
+ file_type_application,
+ file_type_dll,
+ file_type_driver,
+ file_type_font,
+ file_type_vxd,
+ file_type_static_lib
+ };
+
+public:
+ //Default constructor
+ file_version_info();
+ //Constructor from Windows fixed version info structure
+ explicit file_version_info(const pe_win::vs_fixedfileinfo& info);
+
+public: //Getters
+ //Returns true if file is debug-built
+ bool is_debug() const;
+ //Returns true if file is prerelease
+ bool is_prerelease() const;
+ //Returns true if file is patched
+ bool is_patched() const;
+ //Returns true if private build
+ bool is_private_build() const;
+ //Returns true if special build
+ bool is_special_build() const;
+ //Returns true if info inferred
+ bool is_info_inferred() const;
+ //Retuens file flags (raw DWORD)
+ uint32_t get_file_flags() const;
+
+ //Returns file version most significant DWORD
+ uint32_t get_file_version_ms() const;
+ //Returns file version least significant DWORD
+ uint32_t get_file_version_ls() const;
+ //Returns product version most significant DWORD
+ uint32_t get_product_version_ms() const;
+ //Returns product version least significant DWORD
+ uint32_t get_product_version_ls() const;
+
+ //Returns file OS type (raw DWORD)
+ uint32_t get_file_os_raw() const;
+ //Returns file OS type
+ file_os_type get_file_os() const;
+
+ //Returns file type (raw DWORD)
+ uint32_t get_file_type_raw() const;
+ //Returns file type
+ file_type get_file_type() const;
+
+ //Returns file subtype (usually non-zero for drivers and fonts)
+ uint32_t get_file_subtype() const;
+
+ //Returns file date most significant DWORD
+ uint32_t get_file_date_ms() const;
+ //Returns file date least significant DWORD
+ uint32_t get_file_date_ls() const;
+
+ //Returns file version string
+ template<typename T>
+ const std::basic_string<T> get_file_version_string() const
+ {
+ return get_version_string<T>(file_version_ms_, file_version_ls_);
+ }
+
+ //Returns product version string
+ template<typename T>
+ const std::basic_string<T> get_product_version_string() const
+ {
+ return get_version_string<T>(product_version_ms_, product_version_ls_);
+ }
+
+public: //Setters
+ //Sets if file is debug-built
+ void set_debug(bool debug);
+ //Sets if file is prerelease
+ void set_prerelease(bool prerelease);
+ //Sets if file is patched
+ void set_patched(bool patched);
+ //Sets if private build
+ void set_private_build(bool private_build);
+ //Sets if special build
+ void set_special_build(bool special_build);
+ //Sets if info inferred
+ void set_info_inferred(bool info_inferred);
+ //Sets flags (raw DWORD)
+ void set_file_flags(uint32_t file_flags);
+
+ //Sets file version most significant DWORD
+ void set_file_version_ms(uint32_t file_version_ms);
+ //Sets file version least significant DWORD
+ void set_file_version_ls(uint32_t file_version_ls);
+ //Sets product version most significant DWORD
+ void set_product_version_ms(uint32_t product_version_ms);
+ //Sets product version least significant DWORD
+ void set_product_version_ls(uint32_t product_version_ls);
+
+ //Sets file OS type (raw DWORD)
+ void set_file_os_raw(uint32_t file_os);
+ //Sets file OS type
+ void set_file_os(file_os_type file_os);
+
+ //Sets file type (raw DWORD)
+ void set_file_type_raw(uint32_t file_type);
+ //Sets file type
+ void set_file_type(file_type file_type);
+
+ //Sets file subtype (usually non-zero for drivers and fonts)
+ void set_file_subtype(uint32_t file_subtype);
+
+ //Sets file date most significant DWORD
+ void set_file_date_ms(uint32_t file_date_ms);
+ //Sets file date least significant DWORD
+ void set_file_date_ls(uint32_t file_date_ls);
+
+private:
+ //Helper to convert version DWORDs to string
+ template<typename T>
+ static const std::basic_string<T> get_version_string(uint32_t ms, uint32_t ls)
+ {
+ std::basic_stringstream<T> ss;
+ ss << (ms >> 16) << static_cast<T>(L'.')
+ << (ms & 0xFFFF) << static_cast<T>(L'.')
+ << (ls >> 16) << static_cast<T>(L'.')
+ << (ls & 0xFFFF);
+ return ss.str();
+ }
+
+ //Helper to set file flag
+ void set_file_flag(uint32_t flag);
+ //Helper to clear file flag
+ void clear_file_flag(uint32_t flag);
+ //Helper to set or clear file flag
+ void set_file_flag(uint32_t flag, bool set_flag);
+
+ uint32_t file_version_ms_, file_version_ls_,
+ product_version_ms_, product_version_ls_;
+ uint32_t file_flags_;
+ uint32_t file_os_;
+ uint32_t file_type_, file_subtype_;
+ uint32_t file_date_ms_, file_date_ls_;
+};
+}
diff --git a/tools/pe_bliss/message_table.cpp b/tools/pe_bliss/message_table.cpp
new file mode 100644
index 0000000000..909be5d494
--- /dev/null
+++ b/tools/pe_bliss/message_table.cpp
@@ -0,0 +1,81 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "message_table.h"
+#include "utils.h"
+
+namespace pe_bliss
+{
+//Default constructor
+message_table_item::message_table_item()
+ :unicode_(false)
+{}
+
+//Constructor from ANSI string
+message_table_item::message_table_item(const std::string& str)
+ :unicode_(false), ansi_str_(str)
+{
+ pe_utils::strip_nullbytes(ansi_str_);
+}
+
+//Constructor from UNICODE string
+message_table_item::message_table_item(const std::wstring& str)
+ :unicode_(true), unicode_str_(str)
+{
+ pe_utils::strip_nullbytes(unicode_str_);
+}
+
+//Returns true if contained string is unicode
+bool message_table_item::is_unicode() const
+{
+ return unicode_;
+}
+
+//Returns ANSI string
+const std::string& message_table_item::get_ansi_string() const
+{
+ return ansi_str_;
+}
+
+//Returns UNICODE string
+const std::wstring& message_table_item::get_unicode_string() const
+{
+ return unicode_str_;
+}
+
+//Sets ANSI string (clears UNICODE one)
+void message_table_item::set_string(const std::string& str)
+{
+ ansi_str_ = str;
+ pe_utils::strip_nullbytes(ansi_str_);
+ unicode_str_.clear();
+ unicode_ = false;
+}
+
+//Sets UNICODE string (clears ANSI one)
+void message_table_item::set_string(const std::wstring& str)
+{
+ unicode_str_ = str;
+ pe_utils::strip_nullbytes(unicode_str_);
+ ansi_str_.clear();
+ unicode_ = true;
+}
+}
diff --git a/tools/pe_bliss/message_table.h b/tools/pe_bliss/message_table.h
new file mode 100644
index 0000000000..5a3feb32c1
--- /dev/null
+++ b/tools/pe_bliss/message_table.h
@@ -0,0 +1,56 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <string>
+#include <map>
+#include "stdint_defs.h"
+
+namespace pe_bliss
+{
+//Structure representing message table string
+class message_table_item
+{
+public:
+ //Default constructor
+ message_table_item();
+ //Constructors from ANSI and UNICODE strings
+ explicit message_table_item(const std::string& str);
+ explicit message_table_item(const std::wstring& str);
+
+ //Returns true if string is UNICODE
+ bool is_unicode() const;
+ //Returns ANSI string
+ const std::string& get_ansi_string() const;
+ //Returns UNICODE string
+ const std::wstring& get_unicode_string() const;
+
+public:
+ //Sets ANSI or UNICODE string
+ void set_string(const std::string& str);
+ void set_string(const std::wstring& str);
+
+private:
+ bool unicode_;
+ std::string ansi_str_;
+ std::wstring unicode_str_;
+};
+}
diff --git a/tools/pe_bliss/pe_base.cpp b/tools/pe_bliss/pe_base.cpp
new file mode 100644
index 0000000000..97baa17cb3
--- /dev/null
+++ b/tools/pe_bliss/pe_base.cpp
@@ -0,0 +1,1680 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <string>
+#include <vector>
+#include <istream>
+#include <ostream>
+#include <algorithm>
+#include <cmath>
+#include <set>
+#include <string.h>
+#include "pe_exception.h"
+#include "pe_base.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//Constructor
+pe_base::pe_base(std::istream& file, const pe_properties& props, bool read_debug_raw_data)
+{
+ props_ = props.duplicate().release();
+
+ //Save istream state
+ std::ios_base::iostate state = file.exceptions();
+ std::streamoff old_offset = file.tellg();
+
+ try
+ {
+ file.exceptions(std::ios::goodbit);
+ //Read DOS header, PE headers and section data
+ read_dos_header(file);
+ read_pe(file, read_debug_raw_data);
+ }
+ catch(const std::exception&)
+ {
+ //If something went wrong, restore istream state
+ file.seekg(old_offset);
+ file.exceptions(state);
+ file.clear();
+ //Rethrow
+ throw;
+ }
+
+ //Restore istream state
+ file.seekg(old_offset);
+ file.exceptions(state);
+ file.clear();
+}
+
+pe_base::pe_base(const pe_properties& props, uint32_t section_alignment, bool dll, uint16_t subsystem)
+{
+ props_ = props.duplicate().release();
+ props_->create_pe(section_alignment, subsystem);
+
+ has_overlay_ = false;
+ memset(&dos_header_, 0, sizeof(dos_header_));
+
+ dos_header_.e_magic = 0x5A4D; //"MZ"
+ //Magic numbers from MSVC++ build
+ dos_header_.e_maxalloc = 0xFFFF;
+ dos_header_.e_cblp = 0x90;
+ dos_header_.e_cp = 3;
+ dos_header_.e_cparhdr = 4;
+ dos_header_.e_sp = 0xB8;
+ dos_header_.e_lfarlc = 64;
+
+ set_characteristics(image_file_executable_image | image_file_relocs_stripped);
+
+ if(get_pe_type() == pe_type_32)
+ set_characteristics_flags(image_file_32bit_machine);
+
+ if(dll)
+ set_characteristics_flags(image_file_dll);
+
+ set_subsystem_version(5, 1); //WinXP
+ set_os_version(5, 1); //WinXP
+}
+
+pe_base::pe_base(const pe_base& pe)
+ :dos_header_(pe.dos_header_),
+ rich_overlay_(pe.rich_overlay_),
+ sections_(pe.sections_),
+ has_overlay_(pe.has_overlay_),
+ full_headers_data_(pe.full_headers_data_),
+ debug_data_(pe.debug_data_),
+ props_(0)
+{
+ props_ = pe.props_->duplicate().release();
+}
+
+pe_base& pe_base::operator=(const pe_base& pe)
+{
+ dos_header_ = pe.dos_header_;
+ rich_overlay_ = pe.rich_overlay_;
+ sections_ = pe.sections_;
+ has_overlay_ = pe.has_overlay_;
+ full_headers_data_ = pe.full_headers_data_;
+ debug_data_ = pe.debug_data_;
+ delete props_;
+ props_ = 0;
+ props_ = pe.props_->duplicate().release();
+
+ return *this;
+}
+
+pe_base::~pe_base()
+{
+ delete props_;
+}
+
+//Returns dos header
+const image_dos_header& pe_base::get_dos_header() const
+{
+ return dos_header_;
+}
+
+//Returns dos header
+image_dos_header& pe_base::get_dos_header()
+{
+ return dos_header_;
+}
+
+//Returns PE headers start position (e_lfanew)
+int32_t pe_base::get_pe_header_start() const
+{
+ return dos_header_.e_lfanew;
+}
+
+//Strips MSVC stub overlay
+void pe_base::strip_stub_overlay()
+{
+ rich_overlay_.clear();
+}
+
+//Fills MSVC stub overlay with character c
+void pe_base::fill_stub_overlay(char c)
+{
+ if(rich_overlay_.length())
+ rich_overlay_.assign(rich_overlay_.length(), c);
+}
+
+//Sets stub MSVS overlay
+void pe_base::set_stub_overlay(const std::string& data)
+{
+ rich_overlay_ = data;
+}
+
+//Returns stub overlay
+const std::string& pe_base::get_stub_overlay() const
+{
+ return rich_overlay_;
+}
+
+//Realigns all sections
+void pe_base::realign_all_sections()
+{
+ for(unsigned int i = 0; i < sections_.size(); i++)
+ realign_section(i);
+}
+
+//Returns number of sections from PE header
+uint16_t pe_base::get_number_of_sections() const
+{
+ return props_->get_number_of_sections();
+}
+
+//Updates number of sections in PE header
+uint16_t pe_base::update_number_of_sections()
+{
+ uint16_t new_number = static_cast<uint16_t>(sections_.size());
+ props_->set_number_of_sections(new_number);
+ return new_number;
+}
+
+//Returns section alignment
+uint32_t pe_base::get_section_alignment() const
+{
+ return props_->get_section_alignment();
+}
+
+//Returns image sections list
+section_list& pe_base::get_image_sections()
+{
+ return sections_;
+}
+
+//Returns image sections list
+const section_list& pe_base::get_image_sections() const
+{
+ return sections_;
+}
+
+//Realigns section by index
+void pe_base::realign_section(uint32_t index)
+{
+ //Check index
+ if(sections_.size() <= index)
+ throw pe_exception("Section not found", pe_exception::section_not_found);
+
+ //Get section iterator
+ section_list::iterator it = sections_.begin() + index;
+ section& s = *it;
+
+ //Calculate, how many null bytes we have in the end of raw section data
+ std::size_t strip = 0;
+ for(std::size_t i = (*it).get_raw_data().length(); i >= 1; --i)
+ {
+ if(s.get_raw_data()[i - 1] == 0)
+ strip++;
+ else
+ break;
+ }
+
+ if(it == sections_.end() - 1) //If we're realigning the last section
+ {
+ //We can strip ending null bytes
+ s.set_size_of_raw_data(static_cast<uint32_t>(s.get_raw_data().length() - strip));
+ s.get_raw_data().resize(s.get_raw_data().length() - strip, 0);
+ }
+ else
+ {
+ //Else just set size of raw data
+ uint32_t raw_size_aligned = s.get_aligned_raw_size(get_file_alignment());
+ s.set_size_of_raw_data(raw_size_aligned);
+ s.get_raw_data().resize(raw_size_aligned, 0);
+ }
+}
+
+//Returns file alignment
+uint32_t pe_base::get_file_alignment() const
+{
+ return props_->get_file_alignment();
+}
+
+//Sets file alignment
+void pe_base::set_file_alignment(uint32_t alignment)
+{
+ //Check alignment
+ if(alignment < minimum_file_alignment)
+ throw pe_exception("File alignment can't be less than 512", pe_exception::incorrect_file_alignment);
+
+ if(!pe_utils::is_power_of_2(alignment))
+ throw pe_exception("File alignment must be a power of 2", pe_exception::incorrect_file_alignment);
+
+ if(alignment > get_section_alignment())
+ throw pe_exception("File alignment must be <= section alignment", pe_exception::incorrect_file_alignment);
+
+ //Set file alignment without any additional checks
+ set_file_alignment_unchecked(alignment);
+}
+
+//Returns size of image
+uint32_t pe_base::get_size_of_image() const
+{
+ return props_->get_size_of_image();
+}
+
+//Returns image entry point
+uint32_t pe_base::get_ep() const
+{
+ return props_->get_ep();
+}
+
+//Sets image entry point (just a value of PE header)
+void pe_base::set_ep(uint32_t new_ep)
+{
+ props_->set_ep(new_ep);
+}
+
+//Returns number of RVA and sizes (number of DATA_DIRECTORY entries)
+uint32_t pe_base::get_number_of_rvas_and_sizes() const
+{
+ return props_->get_number_of_rvas_and_sizes();
+}
+
+//Sets number of RVA and sizes (number of DATA_DIRECTORY entries)
+void pe_base::set_number_of_rvas_and_sizes(uint32_t number)
+{
+ props_->set_number_of_rvas_and_sizes(number);
+}
+
+//Returns PE characteristics
+uint16_t pe_base::get_characteristics() const
+{
+ return props_->get_characteristics();
+}
+
+//Sets PE characteristics (a value inside header)
+void pe_base::set_characteristics(uint16_t ch)
+{
+ props_->set_characteristics(ch);
+}
+
+//Returns section from RVA
+section& pe_base::section_from_rva(uint32_t rva)
+{
+ //Search for section
+ for(section_list::iterator i = sections_.begin(); i != sections_.end(); ++i)
+ {
+ section& s = *i;
+ //Return section if found
+ if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()))
+ return s;
+ }
+
+ throw pe_exception("No section found by presented address", pe_exception::no_section_found);
+}
+
+//Returns section from RVA
+const section& pe_base::section_from_rva(uint32_t rva) const
+{
+ //Search for section
+ for(section_list::const_iterator i = sections_.begin(); i != sections_.end(); ++i)
+ {
+ const section& s = *i;
+ //Return section if found
+ if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()))
+ return s;
+ }
+
+ throw pe_exception("No section found by presented address", pe_exception::no_section_found);
+}
+
+//Returns section from directory ID
+section& pe_base::section_from_directory(uint32_t directory_id)
+{
+ return section_from_rva(get_directory_rva(directory_id));
+}
+
+//Returns section from directory ID
+const section& pe_base::section_from_directory(uint32_t directory_id) const
+{
+ return section_from_rva(get_directory_rva(directory_id));
+}
+
+//Sets section virtual size (actual for the last one of this PE or for unbound section)
+void pe_base::set_section_virtual_size(section& s, uint32_t vsize)
+{
+ //Check if we're changing virtual size of the last section
+ //Of course, we can change virtual size of section that's not bound to this PE file
+ if(sections_.empty() || std::find_if(sections_.begin(), sections_.end() - 1, section_ptr_finder(s)) != sections_.end() - 1)
+ throw pe_exception("Can't change virtual size of any section, except last one", pe_exception::error_changing_section_virtual_size);
+
+ //If we're setting virtual size to zero
+ if(vsize == 0)
+ {
+ //Check if section is empty
+ if(s.empty())
+ throw pe_exception("Cannot set virtual size of empty section to zero", pe_exception::error_changing_section_virtual_size);
+
+ //Set virtual size equal to aligned size of raw data
+ s.set_virtual_size(s.get_size_of_raw_data());
+ }
+ else
+ {
+ s.set_virtual_size(vsize);
+ }
+
+ //Update image size if we're changing virtual size for the last section of this PE
+ if(!sections_.empty() || &s == &(*(sections_.end() - 1)))
+ update_image_size();
+}
+
+//Expands section raw or virtual size to hold data from specified RVA with specified size
+//Section must be free (not bound to any image)
+//or the last section of this image
+bool pe_base::expand_section(section& s, uint32_t needed_rva, uint32_t needed_size, section_expand_type expand)
+{
+ //Check if we're changing the last section
+ //Of course, we can change the section that's not bound to this PE file
+ if(sections_.empty() || std::find_if(sections_.begin(), sections_.end() - 1, section_ptr_finder(s)) != sections_.end() - 1)
+ throw pe_exception("Can't expand any section, except last one", pe_exception::error_expanding_section);
+
+ //Check if we should expand our section
+ if(expand == expand_section_raw && section_data_length_from_rva(s, needed_rva, section_data_raw) < needed_size)
+ {
+ //Expand section raw data
+ s.get_raw_data().resize(needed_rva - s.get_virtual_address() + needed_size);
+ recalculate_section_sizes(s, false);
+ return true;
+ }
+ else if(expand == expand_section_virtual && section_data_length_from_rva(s, needed_rva, section_data_virtual) < needed_size)
+ {
+ //Expand section virtual data
+ set_section_virtual_size(s, needed_rva - s.get_virtual_address() + needed_size);
+ return true;
+ }
+
+ return false;
+}
+
+//Updates image virtual size
+void pe_base::update_image_size()
+{
+ //Write virtual size of image to headers
+ if(!sections_.empty())
+ set_size_of_image(sections_.back().get_virtual_address() + sections_.back().get_aligned_virtual_size(get_section_alignment()));
+ else
+ set_size_of_image(get_size_of_headers());
+}
+
+//Returns checksum of PE file from header
+uint32_t pe_base::get_checksum() const
+{
+ return props_->get_checksum();
+}
+
+//Sets checksum of PE file
+void pe_base::set_checksum(uint32_t checksum)
+{
+ props_->set_checksum(checksum);
+}
+
+//Returns timestamp of PE file from header
+uint32_t pe_base::get_time_date_stamp() const
+{
+ return props_->get_time_date_stamp();
+}
+
+//Sets timestamp of PE file
+void pe_base::set_time_date_stamp(uint32_t timestamp)
+{
+ props_->set_time_date_stamp(timestamp);
+}
+
+//Returns Machine field value of PE file from header
+uint16_t pe_base::get_machine() const
+{
+ return props_->get_machine();
+}
+
+//Sets Machine field value of PE file
+void pe_base::set_machine(uint16_t machine)
+{
+ props_->set_machine(machine);
+}
+
+//Prepares section before attaching it
+void pe_base::prepare_section(section& s)
+{
+ //Calculate its size of raw data
+ s.set_size_of_raw_data(static_cast<uint32_t>(pe_utils::align_up(s.get_raw_data().length(), get_file_alignment())));
+
+ //Check section virtual and raw size
+ if(!s.get_size_of_raw_data() && !s.get_virtual_size())
+ throw pe_exception("Virtual and Physical sizes of section can't be 0 at the same time", pe_exception::zero_section_sizes);
+
+ //If section virtual size is zero
+ if(!s.get_virtual_size())
+ {
+ s.set_virtual_size(s.get_size_of_raw_data());
+ }
+ else
+ {
+ //Else calculate its virtual size
+ s.set_virtual_size(
+ std::max<uint32_t>(pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment()),
+ pe_utils::align_up(s.get_virtual_size(), get_section_alignment())));
+ }
+}
+
+//Adds section to image
+section& pe_base::add_section(section s)
+{
+ if(sections_.size() >= maximum_number_of_sections)
+ throw pe_exception("Maximum number of sections has been reached", pe_exception::no_more_sections_can_be_added);
+
+ //Prepare section before adding it
+ prepare_section(s);
+
+ //Calculate section virtual address
+ if(!sections_.empty())
+ {
+ s.set_virtual_address(pe_utils::align_up(sections_.back().get_virtual_address() + sections_.back().get_aligned_virtual_size(get_section_alignment()), get_section_alignment()));
+
+ //We should align last section raw size, if it wasn't aligned
+ section& last = sections_.back();
+ last.set_size_of_raw_data(static_cast<uint32_t>(pe_utils::align_up(last.get_raw_data().length(), get_file_alignment())));
+ }
+ else
+ {
+ s.set_virtual_address(
+ s.get_virtual_address() == 0
+ ? pe_utils::align_up(get_size_of_headers(), get_section_alignment())
+ : pe_utils::align_up(s.get_virtual_address(), get_section_alignment()));
+ }
+
+ //Add section to the end of section list
+ sections_.push_back(s);
+ //Set number of sections in PE header
+ set_number_of_sections(static_cast<uint16_t>(sections_.size()));
+ //Recalculate virtual size of image
+ set_size_of_image(get_size_of_image() + s.get_aligned_virtual_size(get_section_alignment()));
+ //Return last section
+ return sections_.back();
+}
+
+//Returns true if sectios "s" is already attached to this PE file
+bool pe_base::section_attached(const section& s) const
+{
+ return sections_.end() != std::find_if(sections_.begin(), sections_.end(), section_ptr_finder(s));
+}
+
+//Returns true if directory exists
+bool pe_base::directory_exists(uint32_t id) const
+{
+ return props_->directory_exists(id);
+}
+
+//Removes directory
+void pe_base::remove_directory(uint32_t id)
+{
+ props_->remove_directory(id);
+}
+
+//Returns directory RVA
+uint32_t pe_base::get_directory_rva(uint32_t id) const
+{
+ return props_->get_directory_rva(id);
+}
+
+//Returns directory size
+uint32_t pe_base::get_directory_size(uint32_t id) const
+{
+ return props_->get_directory_size(id);
+}
+
+//Sets directory RVA (just a value of PE header, no moving occurs)
+void pe_base::set_directory_rva(uint32_t id, uint32_t rva)
+{
+ return props_->set_directory_rva(id, rva);
+}
+
+//Sets directory size (just a value of PE header, no moving occurs)
+void pe_base::set_directory_size(uint32_t id, uint32_t size)
+{
+ return props_->set_directory_size(id, size);
+}
+
+//Strips only zero DATA_DIRECTORY entries to count = min_count
+//Returns resulting number of data directories
+//strip_iat_directory - if true, even not empty IAT directory will be stripped
+uint32_t pe_base::strip_data_directories(uint32_t min_count, bool strip_iat_directory)
+{
+ return props_->strip_data_directories(min_count, strip_iat_directory);
+}
+
+//Returns true if image has import directory
+bool pe_base::has_imports() const
+{
+ return directory_exists(image_directory_entry_import);
+}
+
+//Returns true if image has export directory
+bool pe_base::has_exports() const
+{
+ return directory_exists(image_directory_entry_export);
+}
+
+//Returns true if image has resource directory
+bool pe_base::has_resources() const
+{
+ return directory_exists(image_directory_entry_resource);
+}
+
+//Returns true if image has security directory
+bool pe_base::has_security() const
+{
+ return directory_exists(image_directory_entry_security);
+}
+
+//Returns true if image has relocations
+bool pe_base::has_reloc() const
+{
+ return directory_exists(image_directory_entry_basereloc) && !(get_characteristics() & image_file_relocs_stripped);
+}
+
+//Returns true if image has TLS directory
+bool pe_base::has_tls() const
+{
+ return directory_exists(image_directory_entry_tls);
+}
+
+//Returns true if image has config directory
+bool pe_base::has_config() const
+{
+ return directory_exists(image_directory_entry_load_config);
+}
+
+//Returns true if image has bound import directory
+bool pe_base::has_bound_import() const
+{
+ return directory_exists(image_directory_entry_bound_import);
+}
+
+//Returns true if image has delay import directory
+bool pe_base::has_delay_import() const
+{
+ return directory_exists(image_directory_entry_delay_import);
+}
+
+//Returns true if image has COM directory
+bool pe_base::is_dotnet() const
+{
+ return directory_exists(image_directory_entry_com_descriptor);
+}
+
+//Returns true if image has exception directory
+bool pe_base::has_exception_directory() const
+{
+ return directory_exists(image_directory_entry_exception);
+}
+
+//Returns true if image has debug directory
+bool pe_base::has_debug() const
+{
+ return directory_exists(image_directory_entry_debug);
+}
+
+//Returns corresponding section data pointer from RVA inside section "s" (checks bounds)
+char* pe_base::section_data_from_rva(section& s, uint32_t rva)
+{
+ //Check if RVA is inside section "s"
+ if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()))
+ {
+ if(s.get_raw_data().empty())
+ throw pe_exception("Section raw data is empty and cannot be changed", pe_exception::section_is_empty);
+
+ return &s.get_raw_data()[rva - s.get_virtual_address()];
+ }
+
+ throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists);
+}
+
+//Returns corresponding section data pointer from RVA inside section "s" (checks bounds)
+const char* pe_base::section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype) const
+{
+ //Check if RVA is inside section "s"
+ if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()))
+ return (datatype == section_data_raw ? s.get_raw_data().data() : s.get_virtual_data(get_section_alignment()).c_str()) + rva - s.get_virtual_address();
+
+ throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists);
+}
+
+//Returns section TOTAL RAW/VIRTUAL data length from RVA inside section
+uint32_t pe_base::section_data_length_from_rva(uint32_t rva, section_data_type datatype, bool include_headers) const
+{
+ //if RVA is inside of headers and we're searching them too...
+ if(include_headers && rva < full_headers_data_.length())
+ return static_cast<unsigned long>(full_headers_data_.length());
+
+ const section& s = section_from_rva(rva);
+ return static_cast<unsigned long>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment()));
+}
+
+//Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32
+uint32_t pe_base::section_data_length_from_va(uint32_t va, section_data_type datatype, bool include_headers) const
+{
+ return section_data_length_from_rva(va_to_rva(va), datatype, include_headers);
+}
+
+//Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32/PE64
+uint32_t pe_base::section_data_length_from_va(uint64_t va, section_data_type datatype, bool include_headers) const
+{
+ return section_data_length_from_rva(va_to_rva(va), datatype, include_headers);
+}
+
+//Returns section remaining RAW/VIRTUAL data length from RVA "rva_inside" to the end of section containing RVA "rva"
+uint32_t pe_base::section_data_length_from_rva(uint32_t rva, uint32_t rva_inside, section_data_type datatype, bool include_headers) const
+{
+ //if RVAs are inside of headers and we're searching them too...
+ if(include_headers && rva < full_headers_data_.length() && rva_inside < full_headers_data_.length())
+ return static_cast<unsigned long>(full_headers_data_.length() - rva_inside);
+
+ const section& s = section_from_rva(rva);
+ if(rva_inside < s.get_virtual_address())
+ throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists);
+
+ //Calculate remaining length of section data from "rva" address
+ long length = static_cast<long>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment()))
+ + s.get_virtual_address() - rva_inside;
+
+ if(length < 0)
+ return 0;
+
+ return static_cast<unsigned long>(length);
+}
+
+//Returns section remaining RAW/VIRTUAL data length from VA "va_inside" to the end of section containing VA "va" for PE32
+uint32_t pe_base::section_data_length_from_va(uint32_t va, uint32_t va_inside, section_data_type datatype, bool include_headers) const
+{
+ return section_data_length_from_rva(va_to_rva(va), va_to_rva(va_inside), datatype, include_headers);
+}
+
+//Returns section remaining RAW/VIRTUAL data length from VA "va_inside" to the end of section containing VA "va" for PE32/PE64
+uint32_t pe_base::section_data_length_from_va(uint64_t va, uint64_t va_inside, section_data_type datatype, bool include_headers) const
+{
+ return section_data_length_from_rva(va_to_rva(va), va_to_rva(va_inside), datatype, include_headers);
+}
+
+//Returns section remaining RAW/VIRTUAL data length from RVA to the end of section "s" (checks bounds)
+uint32_t pe_base::section_data_length_from_rva(const section& s, uint32_t rva_inside, section_data_type datatype) const
+{
+ //Check rva_inside
+ if(rva_inside >= s.get_virtual_address() && rva_inside < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()))
+ {
+ //Calculate remaining length of section data from "rva" address
+ int32_t length = static_cast<int32_t>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment()))
+ + s.get_virtual_address() - rva_inside;
+
+ if(length < 0)
+ return 0;
+
+ return static_cast<uint32_t>(length);
+ }
+
+ throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists);
+}
+
+//Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32 (checks bounds)
+uint32_t pe_base::section_data_length_from_va(const section& s, uint32_t va_inside, section_data_type datatype) const
+{
+ return section_data_length_from_rva(s, va_to_rva(va_inside), datatype);
+}
+
+//Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32/PE64 (checks bounds)
+uint32_t pe_base::section_data_length_from_va(const section& s, uint64_t va_inside, section_data_type datatype) const
+{
+ return section_data_length_from_rva(s, va_to_rva(va_inside), datatype);
+}
+
+//Returns corresponding section data pointer from RVA inside section
+char* pe_base::section_data_from_rva(uint32_t rva, bool include_headers)
+{
+ //if RVA is inside of headers and we're searching them too...
+ if(include_headers && rva < full_headers_data_.length())
+ return &full_headers_data_[rva];
+
+ section& s = section_from_rva(rva);
+
+ if(s.get_raw_data().empty())
+ throw pe_exception("Section raw data is empty and cannot be changed", pe_exception::section_is_empty);
+
+ return &s.get_raw_data()[rva - s.get_virtual_address()];
+}
+
+//Returns corresponding section data pointer from RVA inside section
+const char* pe_base::section_data_from_rva(uint32_t rva, section_data_type datatype, bool include_headers) const
+{
+ //if RVA is inside of headers and we're searching them too...
+ if(include_headers && rva < full_headers_data_.length())
+ return &full_headers_data_[rva];
+
+ const section& s = section_from_rva(rva);
+ return (datatype == section_data_raw ? s.get_raw_data().data() : s.get_virtual_data(get_section_alignment()).c_str()) + rva - s.get_virtual_address();
+}
+
+//Reads DOS headers from istream
+void pe_base::read_dos_header(std::istream& file, image_dos_header& header)
+{
+ //Check istream flags
+ if(file.bad() || file.eof())
+ throw pe_exception("PE file stream is bad or closed.", pe_exception::bad_pe_file);
+
+ //Read DOS header and check istream
+ file.read(reinterpret_cast<char*>(&header), sizeof(image_dos_header));
+ if(file.bad() || file.eof())
+ throw pe_exception("Unable to read IMAGE_DOS_HEADER", pe_exception::bad_dos_header);
+
+ //Check DOS header magic
+ if(header.e_magic != 0x5a4d) //"MZ"
+ throw pe_exception("IMAGE_DOS_HEADER signature is incorrect", pe_exception::bad_dos_header);
+}
+
+//Reads DOS headers from istream
+void pe_base::read_dos_header(std::istream& file)
+{
+ read_dos_header(file, dos_header_);
+}
+
+//Reads PE image from istream
+void pe_base::read_pe(std::istream& file, bool read_debug_raw_data)
+{
+ //Get istream size
+ std::streamoff filesize = pe_utils::get_file_size(file);
+
+ //Check if PE header is DWORD-aligned
+ if((dos_header_.e_lfanew % sizeof(uint32_t)) != 0)
+ throw pe_exception("PE header is not DWORD-aligned", pe_exception::bad_dos_header);
+
+ //Seek to NT headers
+ file.seekg(dos_header_.e_lfanew);
+ if(file.bad() || file.fail())
+ throw pe_exception("Cannot reach IMAGE_NT_HEADERS", pe_exception::image_nt_headers_not_found);
+
+ //Read NT headers
+ file.read(get_nt_headers_ptr(), get_sizeof_nt_header() - sizeof(image_data_directory) * image_numberof_directory_entries);
+ if(file.bad() || file.eof())
+ throw pe_exception("Error reading IMAGE_NT_HEADERS", pe_exception::error_reading_image_nt_headers);
+
+ //Check PE signature
+ if(get_pe_signature() != 0x4550) //"PE"
+ throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect);
+
+ //Check number of directories
+ if(get_number_of_rvas_and_sizes() > image_numberof_directory_entries)
+ set_number_of_rvas_and_sizes(image_numberof_directory_entries);
+
+ if(get_number_of_rvas_and_sizes() > 0)
+ {
+ //Read data directory headers, if any
+ file.read(get_nt_headers_ptr() + (get_sizeof_nt_header() - sizeof(image_data_directory) * image_numberof_directory_entries), sizeof(image_data_directory) * get_number_of_rvas_and_sizes());
+ if(file.bad() || file.eof())
+ throw pe_exception("Error reading DATA_DIRECTORY headers", pe_exception::error_reading_data_directories);
+ }
+
+ //Check section number
+ //Images with zero section number accepted
+ if(get_number_of_sections() > maximum_number_of_sections)
+ throw pe_exception("Incorrect number of sections", pe_exception::section_number_incorrect);
+
+ //Check PE magic
+ if(get_magic() != get_needed_magic())
+ throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect);
+
+ //Check section alignment
+ if(!pe_utils::is_power_of_2(get_section_alignment()))
+ throw pe_exception("Incorrect section alignment", pe_exception::incorrect_section_alignment);
+
+ //Check file alignment
+ if(!pe_utils::is_power_of_2(get_file_alignment()))
+ throw pe_exception("Incorrect file alignment", pe_exception::incorrect_file_alignment);
+
+ if(get_file_alignment() != get_section_alignment() && (get_file_alignment() < minimum_file_alignment || get_file_alignment() > get_section_alignment()))
+ throw pe_exception("Incorrect file alignment", pe_exception::incorrect_file_alignment);
+
+ //Check size of image
+ if(pe_utils::align_up(get_size_of_image(), get_section_alignment()) == 0)
+ throw pe_exception("Incorrect size of image", pe_exception::incorrect_size_of_image);
+
+ //Read rich data overlay / DOS stub (if any)
+ if(static_cast<uint32_t>(dos_header_.e_lfanew) > sizeof(image_dos_header))
+ {
+ rich_overlay_.resize(dos_header_.e_lfanew - sizeof(image_dos_header));
+ file.seekg(sizeof(image_dos_header));
+ file.read(&rich_overlay_[0], dos_header_.e_lfanew - sizeof(image_dos_header));
+ if(file.bad() || file.eof())
+ throw pe_exception("Error reading 'Rich' & 'DOS stub' overlay", pe_exception::error_reading_overlay);
+ }
+
+ //Calculate first section raw position
+ //Sum is safe here
+ uint32_t first_section = dos_header_.e_lfanew + get_size_of_optional_header() + sizeof(image_file_header) + sizeof(uint32_t) /* Signature */;
+
+ if(get_number_of_sections() > 0)
+ {
+ //Go to first section
+ file.seekg(first_section);
+ if(file.bad() || file.fail())
+ throw pe_exception("Cannot reach section headers", pe_exception::image_section_headers_not_found);
+ }
+
+ uint32_t last_raw_size = 0;
+
+ //Read all sections
+ for(int i = 0; i < get_number_of_sections(); i++)
+ {
+ section s;
+ //Read section header
+ file.read(reinterpret_cast<char*>(&s.get_raw_header()), sizeof(image_section_header));
+ if(file.bad() || file.eof())
+ throw pe_exception("Error reading section header", pe_exception::error_reading_section_header);
+
+ //Save next section header position
+ std::streamoff next_sect = file.tellg();
+
+ //Check section virtual and raw sizes
+ if(!s.get_size_of_raw_data() && !s.get_virtual_size())
+ throw pe_exception("Virtual and Physical sizes of section can't be 0 at the same time", pe_exception::zero_section_sizes);
+
+ //Check for adequate values of section fields
+ if(!pe_utils::is_sum_safe(s.get_virtual_address(), s.get_virtual_size()) || s.get_virtual_size() > pe_utils::two_gb
+ || !pe_utils::is_sum_safe(s.get_pointer_to_raw_data(), s.get_size_of_raw_data()) || s.get_size_of_raw_data() > pe_utils::two_gb)
+ throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size);
+
+ if(s.get_size_of_raw_data() != 0)
+ {
+ //If section has raw data
+
+ //If section raw data size is greater than virtual, fix it
+ last_raw_size = s.get_size_of_raw_data();
+ if(pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment()) > pe_utils::align_up(s.get_virtual_size(), get_section_alignment()))
+ s.set_size_of_raw_data(s.get_virtual_size());
+
+ //Check virtual and raw section sizes and addresses
+ if(s.get_virtual_address() + pe_utils::align_up(s.get_virtual_size(), get_section_alignment()) > pe_utils::align_up(get_size_of_image(), get_section_alignment())
+ ||
+ pe_utils::align_down(s.get_pointer_to_raw_data(), get_file_alignment()) + s.get_size_of_raw_data() > static_cast<uint32_t>(filesize))
+ throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size);
+
+ //Seek to section raw data
+ file.seekg(pe_utils::align_down(s.get_pointer_to_raw_data(), get_file_alignment()));
+ if(file.bad() || file.fail())
+ throw pe_exception("Cannot reach section data", pe_exception::image_section_data_not_found);
+
+ //Read section raw data
+ s.get_raw_data().resize(s.get_size_of_raw_data());
+ file.read(&s.get_raw_data()[0], s.get_size_of_raw_data());
+ if(file.bad() || file.fail())
+ throw pe_exception("Error reading section data", pe_exception::image_section_data_not_found);
+ }
+
+ //Check virtual address and size of section
+ if(s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()) > pe_utils::align_up(get_size_of_image(), get_section_alignment()))
+ throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size);
+
+ //Save section
+ sections_.push_back(s);
+
+ //Seek to the next section header
+ file.seekg(next_sect);
+ }
+
+ //Check size of headers: SizeOfHeaders can't be larger than first section VA
+ if(!sections_.empty() && get_size_of_headers() > sections_.front().get_virtual_address())
+ throw pe_exception("Incorrect size of headers", pe_exception::incorrect_size_of_headers);
+
+ //If image has more than two sections
+ if(sections_.size() >= 2)
+ {
+ //Check sections virtual sizes
+ for(section_list::const_iterator i = sections_.begin() + 1; i != sections_.end(); ++i)
+ {
+ if((*i).get_virtual_address() != (*(i - 1)).get_virtual_address() + (*(i - 1)).get_aligned_virtual_size(get_section_alignment()))
+ throw pe_exception("Section table is incorrect", pe_exception::image_section_table_incorrect);
+ }
+ }
+
+ //Check if image has overlay in the end of file
+ has_overlay_ = !sections_.empty() && filesize > static_cast<std::streamoff>(sections_.back().get_pointer_to_raw_data() + last_raw_size);
+
+ {
+ //Additionally, read data from the beginning of istream to size of headers
+ file.seekg(0);
+ uint32_t size_of_headers = std::min<uint32_t>(get_size_of_headers(), static_cast<uint32_t>(filesize));
+
+ if(!sections_.empty())
+ {
+ for(section_list::const_iterator i = sections_.begin(); i != sections_.end(); ++i)
+ {
+ if(!(*i).empty())
+ {
+ size_of_headers = std::min<uint32_t>(get_size_of_headers(), (*i).get_pointer_to_raw_data());
+ break;
+ }
+ }
+ }
+
+ full_headers_data_.resize(size_of_headers);
+ file.read(&full_headers_data_[0], size_of_headers);
+ if(file.bad() || file.eof())
+ throw pe_exception("Error reading file", pe_exception::error_reading_file);
+ }
+
+ //Moreover, if there's debug directory, read its raw data for some debug info types
+ while(read_debug_raw_data && has_debug())
+ {
+ try
+ {
+ //Check the length in bytes of the section containing debug directory
+ if(section_data_length_from_rva(get_directory_rva(image_directory_entry_debug), get_directory_rva(image_directory_entry_debug), section_data_virtual, true) < sizeof(image_debug_directory))
+ break;
+
+ unsigned long current_pos = get_directory_rva(image_directory_entry_debug);
+
+ //First IMAGE_DEBUG_DIRECTORY table
+ image_debug_directory directory = section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true);
+
+ //Iterate over all IMAGE_DEBUG_DIRECTORY directories
+ while(directory.PointerToRawData
+ && current_pos < get_directory_rva(image_directory_entry_debug) + get_directory_size(image_directory_entry_debug))
+ {
+ //If we have something to read
+ if((directory.Type == image_debug_type_codeview
+ || directory.Type == image_debug_type_misc
+ || directory.Type == image_debug_type_coff)
+ && directory.SizeOfData)
+ {
+ std::string data;
+ data.resize(directory.SizeOfData);
+ file.seekg(directory.PointerToRawData);
+ file.read(&data[0], directory.SizeOfData);
+ if(file.bad() || file.eof())
+ throw pe_exception("Error reading file", pe_exception::error_reading_file);
+
+ debug_data_.insert(std::make_pair(directory.PointerToRawData, data));
+ }
+
+ //Go to next debug entry
+ current_pos += sizeof(image_debug_directory);
+ directory = section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true);
+ }
+
+ break;
+ }
+ catch(const pe_exception&)
+ {
+ //Don't throw any exception here, if debug info is corrupted or incorrect
+ break;
+ }
+ catch(const std::bad_alloc&)
+ {
+ //Don't throw any exception here, if debug info is corrupted or incorrect
+ break;
+ }
+ }
+}
+
+//Returns PE type of this image
+pe_type pe_base::get_pe_type() const
+{
+ return props_->get_pe_type();
+}
+
+//Returns PE type (PE or PE+) from pe_type enumeration (minimal correctness checks)
+pe_type pe_base::get_pe_type(std::istream& file)
+{
+ //Save state of the istream
+ std::ios_base::iostate state = file.exceptions();
+ std::streamoff old_offset = file.tellg();
+ image_nt_headers32 nt_headers;
+ image_dos_header header;
+
+ try
+ {
+ //Read dos header
+ file.exceptions(std::ios::goodbit);
+ read_dos_header(file, header);
+
+ //Seek to the NT headers start
+ file.seekg(header.e_lfanew);
+ if(file.bad() || file.fail())
+ throw pe_exception("Cannot reach IMAGE_NT_HEADERS", pe_exception::image_nt_headers_not_found);
+
+ //Read NT headers (we're using 32-bit version, because there's no significant differencies between 32 and 64 bit version structures)
+ file.read(reinterpret_cast<char*>(&nt_headers), sizeof(image_nt_headers32) - sizeof(image_data_directory) * image_numberof_directory_entries);
+ if(file.bad() || file.eof())
+ throw pe_exception("Error reading IMAGE_NT_HEADERS", pe_exception::error_reading_image_nt_headers);
+
+ //Check NT headers signature
+ if(nt_headers.Signature != 0x4550) //"PE"
+ throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect);
+
+ //Check NT headers magic
+ if(nt_headers.OptionalHeader.Magic != image_nt_optional_hdr32_magic && nt_headers.OptionalHeader.Magic != image_nt_optional_hdr64_magic)
+ throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect);
+ }
+ catch(const std::exception&)
+ {
+ //If something went wrong, restore istream state
+ file.exceptions(state);
+ file.seekg(old_offset);
+ file.clear();
+ //Retrhow exception
+ throw;
+ }
+
+ //Restore stream state
+ file.exceptions(state);
+ file.seekg(old_offset);
+ file.clear();
+
+ //Determine PE type and return it
+ return nt_headers.OptionalHeader.Magic == image_nt_optional_hdr64_magic ? pe_type_64 : pe_type_32;
+}
+
+//Returns true if image has overlay data at the end of file
+bool pe_base::has_overlay() const
+{
+ return has_overlay_;
+}
+
+//Clears PE characteristics flag
+void pe_base::clear_characteristics_flags(uint16_t flags)
+{
+ set_characteristics(get_characteristics() & ~flags);
+}
+
+//Sets PE characteristics flag
+void pe_base::set_characteristics_flags(uint16_t flags)
+{
+ set_characteristics(get_characteristics() | flags);
+}
+
+//Returns true if PE characteristics flag set
+bool pe_base::check_characteristics_flag(uint16_t flag) const
+{
+ return (get_characteristics() & flag) ? true : false;
+}
+
+//Returns subsystem value
+uint16_t pe_base::get_subsystem() const
+{
+ return props_->get_subsystem();
+}
+
+//Sets subsystem value
+void pe_base::set_subsystem(uint16_t subsystem)
+{
+ props_->set_subsystem(subsystem);
+}
+
+//Returns true if image has console subsystem
+bool pe_base::is_console() const
+{
+ return get_subsystem() == image_subsystem_windows_cui;
+}
+
+//Returns true if image has Windows GUI subsystem
+bool pe_base::is_gui() const
+{
+ return get_subsystem() == image_subsystem_windows_gui;
+}
+
+//Sets required operation system version
+void pe_base::set_os_version(uint16_t major, uint16_t minor)
+{
+ props_->set_os_version(major, minor);
+}
+
+//Returns required operation system version (minor word)
+uint16_t pe_base::get_minor_os_version() const
+{
+ return props_->get_minor_os_version();
+}
+
+//Returns required operation system version (major word)
+uint16_t pe_base::get_major_os_version() const
+{
+ return props_->get_major_os_version();
+}
+
+//Sets required subsystem version
+void pe_base::set_subsystem_version(uint16_t major, uint16_t minor)
+{
+ props_->set_subsystem_version(major, minor);
+}
+
+//Returns required subsystem version (minor word)
+uint16_t pe_base::get_minor_subsystem_version() const
+{
+ return props_->get_minor_subsystem_version();
+}
+
+//Returns required subsystem version (major word)
+uint16_t pe_base::get_major_subsystem_version() const
+{
+ return props_->get_major_subsystem_version();
+}
+
+//Returns corresponding section data pointer from VA inside section "s" for PE32 (checks bounds)
+char* pe_base::section_data_from_va(section& s, uint32_t va) //Always returns raw data
+{
+ return section_data_from_rva(s, va_to_rva(va));
+}
+
+//Returns corresponding section data pointer from VA inside section "s" for PE32 (checks bounds)
+const char* pe_base::section_data_from_va(const section& s, uint32_t va, section_data_type datatype) const
+{
+ return section_data_from_rva(s, va_to_rva(va), datatype);
+}
+
+//Returns corresponding section data pointer from VA inside section for PE32
+char* pe_base::section_data_from_va(uint32_t va, bool include_headers) //Always returns raw data
+{
+ return section_data_from_rva(va_to_rva(va), include_headers);
+}
+
+//Returns corresponding section data pointer from VA inside section for PE32
+const char* pe_base::section_data_from_va(uint32_t va, section_data_type datatype, bool include_headers) const
+{
+ return section_data_from_rva(va_to_rva(va), datatype, include_headers);
+}
+
+//Returns corresponding section data pointer from VA inside section "s" for PE32/PE64 (checks bounds)
+char* pe_base::section_data_from_va(section& s, uint64_t va) //Always returns raw data
+{
+ return section_data_from_rva(s, va_to_rva(va));
+}
+
+//Returns corresponding section data pointer from VA inside section "s" for PE32/PE64 (checks bounds)
+const char* pe_base::section_data_from_va(const section& s, uint64_t va, section_data_type datatype) const
+{
+ return section_data_from_rva(s, va_to_rva(va), datatype);
+}
+
+//Returns corresponding section data pointer from VA inside section for PE32/PE64
+char* pe_base::section_data_from_va(uint64_t va, bool include_headers) //Always returns raw data
+{
+ return section_data_from_rva(va_to_rva(va), include_headers);
+}
+
+//Returns corresponding section data pointer from VA inside section for PE32/PE64
+const char* pe_base::section_data_from_va(uint64_t va, section_data_type datatype, bool include_headers) const
+{
+ return section_data_from_rva(va_to_rva(va), datatype, include_headers);
+}
+
+//Returns section from VA inside it for PE32
+section& pe_base::section_from_va(uint32_t va)
+{
+ return section_from_rva(va_to_rva(va));
+}
+
+//Returns section from VA inside it for PE32/PE64
+section& pe_base::section_from_va(uint64_t va)
+{
+ return section_from_rva(va_to_rva(va));
+}
+
+//Returns section from RVA inside it for PE32
+const section& pe_base::section_from_va(uint32_t va) const
+{
+ return section_from_rva(va_to_rva(va));
+}
+
+//Returns section from RVA inside it for PE32/PE64
+const section& pe_base::section_from_va(uint64_t va) const
+{
+ return section_from_rva(va_to_rva(va));
+}
+
+uint32_t pe_base::va_to_rva(uint32_t va, bool bound_check) const
+{
+ return props_->va_to_rva(va, bound_check);
+}
+
+uint32_t pe_base::va_to_rva(uint64_t va, bool bound_check) const
+{
+ return props_->va_to_rva(va, bound_check);
+}
+
+uint32_t pe_base::rva_to_va_32(uint32_t rva) const
+{
+ return props_->rva_to_va_32(rva);
+}
+
+uint64_t pe_base::rva_to_va_64(uint32_t rva) const
+{
+ return props_->rva_to_va_64(rva);
+}
+
+//Relative Virtual Address (RVA) to Virtual Address (VA) convertion for PE32
+void pe_base::rva_to_va(uint32_t rva, uint32_t& va) const
+{
+ va = rva_to_va_32(rva);
+}
+
+//Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32/PE64
+void pe_base::rva_to_va(uint32_t rva, uint64_t& va) const
+{
+ va = rva_to_va_64(rva);
+}
+
+//Returns section from file offset (4gb max)
+section& pe_base::section_from_file_offset(uint32_t offset)
+{
+ return *file_offset_to_section(offset);
+}
+
+//Returns section from file offset (4gb max)
+const section& pe_base::section_from_file_offset(uint32_t offset) const
+{
+ return *file_offset_to_section(offset);
+}
+
+//Returns section and offset (raw data only) from its start from RVA
+const std::pair<uint32_t, const section*> pe_base::section_and_offset_from_rva(uint32_t rva) const
+{
+ const section& s = section_from_rva(rva);
+ return std::make_pair(rva - s.get_virtual_address(), &s);
+}
+
+//Returns DLL Characteristics
+uint16_t pe_base::get_dll_characteristics() const
+{
+ return props_->get_dll_characteristics();
+}
+
+//Sets DLL Characteristics
+void pe_base::set_dll_characteristics(uint16_t characteristics)
+{
+ props_->set_dll_characteristics(characteristics);
+}
+
+//Returns size of headers
+uint32_t pe_base::get_size_of_headers() const
+{
+ return props_->get_size_of_headers();
+}
+
+//Returns size of optional header
+uint16_t pe_base::get_size_of_optional_header() const
+{
+ return props_->get_size_of_optional_header();
+}
+
+//Returns PE signature
+uint32_t pe_base::get_pe_signature() const
+{
+ return props_->get_pe_signature();
+}
+
+//Returns magic value
+uint32_t pe_base::get_magic() const
+{
+ return props_->get_magic();
+}
+
+//Returns image base for PE32
+void pe_base::get_image_base(uint32_t& base) const
+{
+ base = get_image_base_32();
+}
+
+//Returns image base for PE32 and PE64 respectively
+uint32_t pe_base::get_image_base_32() const
+{
+ return props_->get_image_base_32();
+}
+
+//Sets image base for PE32 and PE64 respectively
+uint64_t pe_base::get_image_base_64() const
+{
+ return props_->get_image_base_64();
+}
+
+//RVA to RAW file offset convertion (4gb max)
+uint32_t pe_base::rva_to_file_offset(uint32_t rva) const
+{
+ //Maybe, RVA is inside PE headers
+ if(rva < get_size_of_headers())
+ return rva;
+
+ const section& s = section_from_rva(rva);
+ return s.get_pointer_to_raw_data() + rva - s.get_virtual_address();
+}
+
+//RAW file offset to RVA convertion (4gb max)
+uint32_t pe_base::file_offset_to_rva(uint32_t offset) const
+{
+ //Maybe, offset is inside PE headers
+ if(offset < get_size_of_headers())
+ return offset;
+
+ const section_list::const_iterator it = file_offset_to_section(offset);
+ return offset - (*it).get_pointer_to_raw_data() + (*it).get_virtual_address();
+}
+
+//RAW file offset to section convertion helper (4gb max)
+section_list::const_iterator pe_base::file_offset_to_section(uint32_t offset) const
+{
+ section_list::const_iterator it = std::find_if(sections_.begin(), sections_.end(), section_by_raw_offset(offset));
+ if(it == sections_.end())
+ throw pe_exception("No section found by presented file offset", pe_exception::no_section_found);
+
+ return it;
+}
+
+//RAW file offset to section convertion helper (4gb max)
+section_list::iterator pe_base::file_offset_to_section(uint32_t offset)
+{
+ section_list::iterator it = std::find_if(sections_.begin(), sections_.end(), section_by_raw_offset(offset));
+ if(it == sections_.end())
+ throw pe_exception("No section found by presented file offset", pe_exception::no_section_found);
+
+ return it;
+}
+
+//RVA from section raw data offset
+uint32_t pe_base::rva_from_section_offset(const section& s, uint32_t raw_offset_from_section_start)
+{
+ return s.get_virtual_address() + raw_offset_from_section_start;
+}
+
+//Returns image base for PE32/PE64
+void pe_base::get_image_base(uint64_t& base) const
+{
+ base = get_image_base_64();
+}
+
+//Sets new image base
+void pe_base::set_image_base(uint32_t base)
+{
+ props_->set_image_base(base);
+}
+
+void pe_base::set_image_base_64(uint64_t base)
+{
+ props_->set_image_base_64(base);
+}
+
+//Sets heap size commit for PE32 and PE64 respectively
+void pe_base::set_heap_size_commit(uint32_t size)
+{
+ props_->set_heap_size_commit(size);
+}
+
+void pe_base::set_heap_size_commit(uint64_t size)
+{
+ props_->set_heap_size_commit(size);
+}
+
+//Sets heap size reserve for PE32 and PE64 respectively
+void pe_base::set_heap_size_reserve(uint32_t size)
+{
+ props_->set_heap_size_reserve(size);
+}
+
+void pe_base::set_heap_size_reserve(uint64_t size)
+{
+ props_->set_heap_size_reserve(size);
+}
+
+//Sets stack size commit for PE32 and PE64 respectively
+void pe_base::set_stack_size_commit(uint32_t size)
+{
+ props_->set_stack_size_commit(size);
+}
+
+void pe_base::set_stack_size_commit(uint64_t size)
+{
+ props_->set_stack_size_commit(size);
+}
+
+//Sets stack size reserve for PE32 and PE64 respectively
+void pe_base::set_stack_size_reserve(uint32_t size)
+{
+ props_->set_stack_size_reserve(size);
+}
+
+void pe_base::set_stack_size_reserve(uint64_t size)
+{
+ props_->set_stack_size_reserve(size);
+}
+
+//Returns heap size commit for PE32 and PE64 respectively
+uint32_t pe_base::get_heap_size_commit_32() const
+{
+ return props_->get_heap_size_commit_32();
+}
+
+uint64_t pe_base::get_heap_size_commit_64() const
+{
+ return props_->get_heap_size_commit_64();
+}
+
+//Returns heap size reserve for PE32 and PE64 respectively
+uint32_t pe_base::get_heap_size_reserve_32() const
+{
+ return props_->get_heap_size_reserve_32();
+}
+
+uint64_t pe_base::get_heap_size_reserve_64() const
+{
+ return props_->get_heap_size_reserve_64();
+}
+
+//Returns stack size commit for PE32 and PE64 respectively
+uint32_t pe_base::get_stack_size_commit_32() const
+{
+ return props_->get_stack_size_commit_32();
+}
+
+uint64_t pe_base::get_stack_size_commit_64() const
+{
+ return props_->get_stack_size_commit_64();
+}
+
+//Returns stack size reserve for PE32 and PE64 respectively
+uint32_t pe_base::get_stack_size_reserve_32() const
+{
+ return props_->get_stack_size_reserve_32();
+}
+
+uint64_t pe_base::get_stack_size_reserve_64() const
+{
+ return props_->get_stack_size_reserve_64();
+}
+
+//Returns heap size commit for PE32
+void pe_base::get_heap_size_commit(uint32_t& size) const
+{
+ size = get_heap_size_commit_32();
+}
+
+//Returns heap size commit for PE32/PE64
+void pe_base::get_heap_size_commit(uint64_t& size) const
+{
+ size = get_heap_size_commit_64();
+}
+
+//Returns heap size reserve for PE32
+void pe_base::get_heap_size_reserve(uint32_t& size) const
+{
+ size = get_heap_size_reserve_32();
+}
+
+//Returns heap size reserve for PE32/PE64
+void pe_base::get_heap_size_reserve(uint64_t& size) const
+{
+ size = get_heap_size_reserve_64();
+}
+
+//Returns stack size commit for PE32
+void pe_base::get_stack_size_commit(uint32_t& size) const
+{
+ size = get_stack_size_commit_32();
+}
+
+//Returns stack size commit for PE32/PE64
+void pe_base::get_stack_size_commit(uint64_t& size) const
+{
+ size = get_stack_size_commit_64();
+}
+
+//Returns stack size reserve for PE32
+void pe_base::get_stack_size_reserve(uint32_t& size) const
+{
+ size = get_stack_size_reserve_32();
+}
+
+//Returns stack size reserve for PE32/PE64
+void pe_base::get_stack_size_reserve(uint64_t& size) const
+{
+ size = get_stack_size_reserve_64();
+}
+
+//Realigns file (changes file alignment)
+void pe_base::realign_file(uint32_t new_file_alignment)
+{
+ //Checks alignment for correctness
+ set_file_alignment(new_file_alignment);
+ realign_all_sections();
+}
+
+//Helper function to recalculate RAW and virtual section sizes and strip it, if necessary
+void pe_base::recalculate_section_sizes(section& s, bool auto_strip)
+{
+ prepare_section(s); //Recalculate section raw addresses
+
+ //Strip RAW size of section, if it is the last one
+ //For all others it must be file-aligned and calculated by prepare_section() call
+ if(auto_strip && !(sections_.empty() || &s == &*(sections_.end() - 1)))
+ {
+ //Strip ending raw data nullbytes to optimize size
+ std::string& raw_data = s.get_raw_data();
+ if(!raw_data.empty())
+ {
+ std::string::size_type i = raw_data.length();
+ for(; i != 1; --i)
+ {
+ if(raw_data[i - 1] != 0)
+ break;
+ }
+
+ raw_data.resize(i);
+ }
+
+ s.set_size_of_raw_data(static_cast<uint32_t>(raw_data.length()));
+ }
+
+ //Can occur only for last section
+ if(pe_utils::align_up(s.get_virtual_size(), get_section_alignment()) < pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment()))
+ set_section_virtual_size(s, pe_utils::align_up(s.get_size_of_raw_data(), get_section_alignment())); //Recalculate section virtual size
+}
+
+//Returns data from the beginning of image
+//Size = SizeOfHeaders
+const std::string& pe_base::get_full_headers_data() const
+{
+ return full_headers_data_;
+}
+
+const pe_base::debug_data_list& pe_base::get_raw_debug_data_list() const
+{
+ return debug_data_;
+}
+
+//Sets number of sections
+void pe_base::set_number_of_sections(uint16_t number)
+{
+ props_->set_number_of_sections(number);
+}
+
+//Sets size of image
+void pe_base::set_size_of_image(uint32_t size)
+{
+ props_->set_size_of_image(size);
+}
+
+//Sets size of headers
+void pe_base::set_size_of_headers(uint32_t size)
+{
+ props_->set_size_of_headers(size);
+}
+
+//Sets size of optional headers
+void pe_base::set_size_of_optional_header(uint16_t size)
+{
+ props_->set_size_of_optional_header(size);
+}
+
+//Returns nt headers data pointer
+char* pe_base::get_nt_headers_ptr()
+{
+ return props_->get_nt_headers_ptr();
+}
+
+//Returns nt headers data pointer
+const char* pe_base::get_nt_headers_ptr() const
+{
+ return props_->get_nt_headers_ptr();
+}
+
+//Returns sizeof() nt headers
+uint32_t pe_base::get_sizeof_nt_header() const
+{
+ return props_->get_sizeof_nt_header();
+}
+
+//Returns sizeof() optional headers
+uint32_t pe_base::get_sizeof_opt_headers() const
+{
+ return props_->get_sizeof_opt_headers();
+}
+
+//Sets file alignment (no checks)
+void pe_base::set_file_alignment_unchecked(uint32_t alignment)
+{
+ props_->set_file_alignment_unchecked(alignment);
+}
+
+//Sets base of code
+void pe_base::set_base_of_code(uint32_t base)
+{
+ props_->set_base_of_code(base);
+}
+
+//Returns base of code
+uint32_t pe_base::get_base_of_code() const
+{
+ return props_->get_base_of_code();
+}
+
+//Returns needed magic of image
+uint32_t pe_base::get_needed_magic() const
+{
+ return props_->get_needed_magic();
+}
+}
diff --git a/tools/pe_bliss/pe_base.h b/tools/pe_bliss/pe_base.h
new file mode 100644
index 0000000000..b5416cf1e2
--- /dev/null
+++ b/tools/pe_bliss/pe_base.h
@@ -0,0 +1,544 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <string>
+#include <vector>
+#include <istream>
+#include <ostream>
+#include <map>
+#include "pe_exception.h"
+#include "pe_structures.h"
+#include "utils.h"
+#include "pe_section.h"
+#include "pe_properties.h"
+
+//Please don't remove this information from header
+//PEBliss 1.0.0
+//(c) DX 2011 - 2012, http://kaimi.ru
+//Free to use for commertial and non-commertial purposes, modification and distribution
+
+// == more important ==
+//TODO: compact import rebuilder
+//TODO: remove sections in the middle
+//== less important ==
+//TODO: relocations that take more than one element (seems to be not possible in Windows PE, but anyway)
+//TODO: delay import directory
+//TODO: write message tables
+//TODO: write string tables
+//TODO: read security information
+//TODO: read full .NET information
+
+namespace pe_bliss
+{
+//Portable executable class
+class pe_base
+{
+public: //CONSTRUCTORS
+ //Constructor from stream
+ pe_base(std::istream& file, const pe_properties& props, bool read_debug_raw_data = true);
+
+ //Constructor of empty PE-file
+ explicit pe_base(const pe_properties& props, uint32_t section_alignment = 0x1000, bool dll = false, uint16_t subsystem = pe_win::image_subsystem_windows_gui);
+
+ pe_base(const pe_base& pe);
+ pe_base& operator=(const pe_base& pe);
+
+public:
+ ~pe_base();
+
+public: //STUB
+ //Strips stub MSVS overlay, if any
+ void strip_stub_overlay();
+ //Fills stub MSVS overlay with specified byte
+ void fill_stub_overlay(char c);
+ //Sets stub MSVS overlay
+ void set_stub_overlay(const std::string& data);
+ //Returns stub overlay contents
+ const std::string& get_stub_overlay() const;
+
+
+public: //DIRECTORIES
+ //Returns true if directory exists
+ bool directory_exists(uint32_t id) const;
+ //Removes directory
+ void remove_directory(uint32_t id);
+
+ //Returns directory RVA
+ uint32_t get_directory_rva(uint32_t id) const;
+ //Returns directory size
+ uint32_t get_directory_size(uint32_t id) const;
+
+ //Sets directory RVA (just a value of PE header, no moving occurs)
+ void set_directory_rva(uint32_t id, uint32_t rva);
+ //Sets directory size (just a value of PE header, no moving occurs)
+ void set_directory_size(uint32_t id, uint32_t size);
+
+ //Strips only zero DATA_DIRECTORY entries to count = min_count
+ //Returns resulting number of data directories
+ //strip_iat_directory - if true, even not empty IAT directory will be stripped
+ uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true);
+
+ //Returns true if image has import directory
+ bool has_imports() const;
+ //Returns true if image has export directory
+ bool has_exports() const;
+ //Returns true if image has resource directory
+ bool has_resources() const;
+ //Returns true if image has security directory
+ bool has_security() const;
+ //Returns true if image has relocations
+ bool has_reloc() const;
+ //Returns true if image has TLS directory
+ bool has_tls() const;
+ //Returns true if image has config directory
+ bool has_config() const;
+ //Returns true if image has bound import directory
+ bool has_bound_import() const;
+ //Returns true if image has delay import directory
+ bool has_delay_import() const;
+ //Returns true if image has COM directory
+ bool is_dotnet() const;
+ //Returns true if image has exception directory
+ bool has_exception_directory() const;
+ //Returns true if image has debug directory
+ bool has_debug() const;
+
+ //Returns subsystem value
+ uint16_t get_subsystem() const;
+ //Sets subsystem value
+ void set_subsystem(uint16_t subsystem);
+ //Returns true if image has console subsystem
+ bool is_console() const;
+ //Returns true if image has Windows GUI subsystem
+ bool is_gui() const;
+
+ //Sets required operation system version
+ void set_os_version(uint16_t major, uint16_t minor);
+ //Returns required operation system version (minor word)
+ uint16_t get_minor_os_version() const;
+ //Returns required operation system version (major word)
+ uint16_t get_major_os_version() const;
+
+ //Sets required subsystem version
+ void set_subsystem_version(uint16_t major, uint16_t minor);
+ //Returns required subsystem version (minor word)
+ uint16_t get_minor_subsystem_version() const;
+ //Returns required subsystem version (major word)
+ uint16_t get_major_subsystem_version() const;
+
+public: //PE HEADER
+ //Returns DOS header
+ const pe_win::image_dos_header& get_dos_header() const;
+ pe_win::image_dos_header& get_dos_header();
+
+ //Returns PE header start (e_lfanew)
+ int32_t get_pe_header_start() const;
+
+ //Returns file alignment
+ uint32_t get_file_alignment() const;
+ //Sets file alignment, checking the correctness of its value
+ void set_file_alignment(uint32_t alignment);
+
+ //Returns size of image
+ uint32_t get_size_of_image() const;
+
+ //Returns image entry point
+ uint32_t get_ep() const;
+ //Sets image entry point (just a value of PE header)
+ void set_ep(uint32_t new_ep);
+
+ //Returns number of RVA and sizes (number of DATA_DIRECTORY entries)
+ uint32_t get_number_of_rvas_and_sizes() const;
+ //Sets number of RVA and sizes (number of DATA_DIRECTORY entries)
+ void set_number_of_rvas_and_sizes(uint32_t number);
+
+ //Returns PE characteristics
+ uint16_t get_characteristics() const;
+ //Sets PE characteristics (a value inside header)
+ void set_characteristics(uint16_t ch);
+ //Clears PE characteristics flag
+ void clear_characteristics_flags(uint16_t flags);
+ //Sets PE characteristics flag
+ void set_characteristics_flags(uint16_t flags);
+ //Returns true if PE characteristics flag set
+ bool check_characteristics_flag(uint16_t flag) const;
+
+ //Returns DLL Characteristics
+ uint16_t get_dll_characteristics() const;
+ //Sets DLL Characteristics
+ void set_dll_characteristics(uint16_t characteristics);
+
+ //Returns size of headers
+ uint32_t get_size_of_headers() const;
+ //Returns size of optional header
+ uint16_t get_size_of_optional_header() const;
+
+ //Returns PE signature
+ uint32_t get_pe_signature() const;
+
+ //Returns magic value
+ uint32_t get_magic() const;
+
+ //Returns image base for PE32 and PE64 respectively
+ uint32_t get_image_base_32() const;
+ void get_image_base(uint32_t& base) const;
+ //Sets image base for PE32 and PE64 respectively
+ uint64_t get_image_base_64() const;
+ void get_image_base(uint64_t& base) const;
+
+ //Sets new image base
+ void set_image_base(uint32_t base);
+ void set_image_base_64(uint64_t base);
+
+ //Sets heap size commit for PE32 and PE64 respectively
+ void set_heap_size_commit(uint32_t size);
+ void set_heap_size_commit(uint64_t size);
+ //Sets heap size reserve for PE32 and PE64 respectively
+ void set_heap_size_reserve(uint32_t size);
+ void set_heap_size_reserve(uint64_t size);
+ //Sets stack size commit for PE32 and PE64 respectively
+ void set_stack_size_commit(uint32_t size);
+ void set_stack_size_commit(uint64_t size);
+ //Sets stack size reserve for PE32 and PE64 respectively
+ void set_stack_size_reserve(uint32_t size);
+ void set_stack_size_reserve(uint64_t size);
+
+ //Returns heap size commit for PE32 and PE64 respectively
+ uint32_t get_heap_size_commit_32() const;
+ void get_heap_size_commit(uint32_t& size) const;
+ uint64_t get_heap_size_commit_64() const;
+ void get_heap_size_commit(uint64_t& size) const;
+ //Returns heap size reserve for PE32 and PE64 respectively
+ uint32_t get_heap_size_reserve_32() const;
+ void get_heap_size_reserve(uint32_t& size) const;
+ uint64_t get_heap_size_reserve_64() const;
+ void get_heap_size_reserve(uint64_t& size) const;
+ //Returns stack size commit for PE32 and PE64 respectively
+ uint32_t get_stack_size_commit_32() const;
+ void get_stack_size_commit(uint32_t& size) const;
+ uint64_t get_stack_size_commit_64() const;
+ void get_stack_size_commit(uint64_t& size) const;
+ //Returns stack size reserve for PE32 and PE64 respectively
+ uint32_t get_stack_size_reserve_32() const;
+ void get_stack_size_reserve(uint32_t& size) const;
+ uint64_t get_stack_size_reserve_64() const;
+ void get_stack_size_reserve(uint64_t& size) const;
+
+ //Updates virtual size of image corresponding to section virtual sizes
+ void update_image_size();
+
+ //Returns checksum of PE file from header
+ uint32_t get_checksum() const;
+ //Sets checksum of PE file
+ void set_checksum(uint32_t checksum);
+
+ //Returns timestamp of PE file from header
+ uint32_t get_time_date_stamp() const;
+ //Sets timestamp of PE file
+ void set_time_date_stamp(uint32_t timestamp);
+
+ //Returns Machine field value of PE file from header
+ uint16_t get_machine() const;
+ //Sets Machine field value of PE file
+ void set_machine(uint16_t machine);
+
+ //Returns data from the beginning of image
+ //Size = SizeOfHeaders
+ const std::string& get_full_headers_data() const;
+
+ typedef std::multimap<uint32_t, std::string> debug_data_list;
+ //Returns raw list of debug data
+ const debug_data_list& get_raw_debug_data_list() const;
+
+ //Reads and checks DOS header
+ static void read_dos_header(std::istream& file, pe_win::image_dos_header& header);
+
+ //Returns sizeof() nt headers
+ uint32_t get_sizeof_nt_header() const;
+ //Returns sizeof() optional headers
+ uint32_t get_sizeof_opt_headers() const;
+ //Returns raw nt headers data pointer
+ const char* get_nt_headers_ptr() const;
+
+ //Sets size of headers (to NT headers)
+ void set_size_of_headers(uint32_t size);
+ //Sets size of optional headers (to NT headers)
+ void set_size_of_optional_header(uint16_t size);
+
+ //Sets base of code
+ void set_base_of_code(uint32_t base);
+ //Returns base of code
+ uint32_t get_base_of_code() const;
+
+public: //ADDRESS CONVERTIONS
+ //Virtual Address (VA) to Relative Virtual Address (RVA) convertions
+ //for PE32 and PE64 respectively
+ //bound_check checks integer overflow
+ uint32_t va_to_rva(uint32_t va, bool bound_check = true) const;
+ uint32_t va_to_rva(uint64_t va, bool bound_check = true) const;
+
+ //Relative Virtual Address (RVA) to Virtual Address (VA) convertions
+ //for PE32 and PE64 respectively
+ uint32_t rva_to_va_32(uint32_t rva) const;
+ void rva_to_va(uint32_t rva, uint32_t& va) const;
+ uint64_t rva_to_va_64(uint32_t rva) const;
+ void rva_to_va(uint32_t rva, uint64_t& va) const;
+
+ //RVA to RAW file offset convertion (4gb max)
+ uint32_t rva_to_file_offset(uint32_t rva) const;
+ //RAW file offset to RVA convertion (4gb max)
+ uint32_t file_offset_to_rva(uint32_t offset) const;
+
+ //RVA from section raw data offset
+ static uint32_t rva_from_section_offset(const section& s, uint32_t raw_offset_from_section_start);
+
+public: //IMAGE SECTIONS
+ //Returns number of sections from PE header
+ uint16_t get_number_of_sections() const;
+
+ //Updates number of sections in PE header
+ uint16_t update_number_of_sections();
+
+ //Returns section alignment
+ uint32_t get_section_alignment() const;
+
+ //Returns section list
+ section_list& get_image_sections();
+ const section_list& get_image_sections() const;
+
+ //Realigns all sections, if you made any changes to sections or alignments
+ void realign_all_sections();
+ //Resligns section with specified index
+ void realign_section(uint32_t index);
+
+ //Returns section from RVA inside it
+ section& section_from_rva(uint32_t rva);
+ const section& section_from_rva(uint32_t rva) const;
+ //Returns section from directory ID
+ section& section_from_directory(uint32_t directory_id);
+ const section& section_from_directory(uint32_t directory_id) const;
+ //Returns section from VA inside it for PE32 and PE64 respectively
+ section& section_from_va(uint32_t va);
+ const section& section_from_va(uint32_t va) const;
+ section& section_from_va(uint64_t va);
+ const section& section_from_va(uint64_t va) const;
+ //Returns section from file offset (4gb max)
+ section& section_from_file_offset(uint32_t offset);
+ const section& section_from_file_offset(uint32_t offset) const;
+
+ //Returns section TOTAL RAW/VIRTUAL data length from RVA inside section
+ //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
+ uint32_t section_data_length_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const;
+ //Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32 and PE64 respectively
+ //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
+ uint32_t section_data_length_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const;
+ uint32_t section_data_length_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const;
+
+ //Returns section remaining RAW/VIRTUAL data length from RVA to the end of section "s" (checks bounds)
+ uint32_t section_data_length_from_rva(const section& s, uint32_t rva_inside, section_data_type datatype = section_data_raw) const;
+ //Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32 and PE64 respectively (checks bounds)
+ uint32_t section_data_length_from_va(const section& s, uint64_t va_inside, section_data_type datatype = section_data_raw) const;
+ uint32_t section_data_length_from_va(const section& s, uint32_t va_inside, section_data_type datatype = section_data_raw) const;
+
+ //Returns section remaining RAW/VIRTUAL data length from RVA "rva_inside" to the end of section containing RVA "rva"
+ //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
+ uint32_t section_data_length_from_rva(uint32_t rva, uint32_t rva_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const;
+ //Returns section remaining RAW/VIRTUAL data length from VA "va_inside" to the end of section containing VA "va" for PE32 and PE64 respectively
+ //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
+ uint32_t section_data_length_from_va(uint32_t va, uint32_t va_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const;
+ uint32_t section_data_length_from_va(uint64_t va, uint64_t va_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const;
+
+ //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
+ //Returns corresponding section data pointer from RVA inside section
+ char* section_data_from_rva(uint32_t rva, bool include_headers = false);
+ const char* section_data_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const;
+ //Returns corresponding section data pointer from VA inside section for PE32 and PE64 respectively
+ char* section_data_from_va(uint32_t va, bool include_headers = false);
+ const char* section_data_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const;
+ char* section_data_from_va(uint64_t va, bool include_headers = false);
+ const char* section_data_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const;
+
+ //Returns corresponding section data pointer from RVA inside section "s" (checks bounds)
+ char* section_data_from_rva(section& s, uint32_t rva);
+ const char* section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw) const;
+ //Returns corresponding section data pointer from VA inside section "s" for PE32 and PE64 respectively (checks bounds)
+ char* section_data_from_va(section& s, uint32_t va); //Always returns raw data
+ const char* section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) const;
+ char* section_data_from_va(section& s, uint64_t va); //Always returns raw data
+ const char* section_data_from_va(const section& s, uint64_t va, section_data_type datatype = section_data_raw) const;
+
+ //Returns corresponding section data pointer from RVA inside section "s" (checks bounds, checks sizes, the most safe function)
+ template<typename T>
+ T section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw) const
+ {
+ if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()) && pe_utils::is_sum_safe(rva, sizeof(T)))
+ {
+ const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(get_section_alignment());
+ //Don't check for underflow here, comparsion is unsigned
+ if(data.size() < rva - s.get_virtual_address() + sizeof(T))
+ throw pe_exception("RVA and requested data size does not exist inside section", pe_exception::rva_not_exists);
+
+ return *reinterpret_cast<const T*>(data.data() + rva - s.get_virtual_address());
+ }
+
+ throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists);
+ }
+
+ //Returns corresponding section data pointer from RVA inside section (checks rva, checks sizes, the most safe function)
+ //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
+ template<typename T>
+ T section_data_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const
+ {
+ //if RVA is inside of headers and we're searching them too...
+ if(include_headers && pe_utils::is_sum_safe(rva, sizeof(T)) && (rva + sizeof(T) < full_headers_data_.length()))
+ return *reinterpret_cast<const T*>(&full_headers_data_[rva]);
+
+ const section& s = section_from_rva(rva);
+ const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(get_section_alignment());
+ //Don't check for underflow here, comparsion is unsigned
+ if(data.size() < rva - s.get_virtual_address() + sizeof(T))
+ throw pe_exception("RVA and requested data size does not exist inside section", pe_exception::rva_not_exists);
+
+ return *reinterpret_cast<const T*>(data.data() + rva - s.get_virtual_address());
+ }
+
+ //Returns corresponding section data pointer from VA inside section "s" (checks bounds, checks sizes, the most safe function)
+ template<typename T>
+ T section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) const
+ {
+ return section_data_from_rva<T>(s, va_to_rva(va), datatype);
+ }
+
+ template<typename T>
+ T section_data_from_va(const section& s, uint64_t va, section_data_type datatype = section_data_raw) const
+ {
+ return section_data_from_rva<T>(s, va_to_rva(va), datatype);
+ }
+
+ //Returns corresponding section data pointer from VA inside section (checks rva, checks sizes, the most safe function)
+ //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too
+ template<typename T>
+ T section_data_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const
+ {
+ return section_data_from_rva<T>(va_to_rva(va), datatype, include_headers);
+ }
+
+ template<typename T>
+ T section_data_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const
+ {
+ return section_data_from_rva<T>(va_to_rva(va), datatype, include_headers);
+ }
+
+ //Returns section and offset (raw data only) from its start from RVA
+ const std::pair<uint32_t, const section*> section_and_offset_from_rva(uint32_t rva) const;
+
+ //Sets virtual size of section "s"
+ //Section must be free (not bound to any image)
+ //or the last section of this image
+ //Function calls update_image_size automatically in second case
+ void set_section_virtual_size(section& s, uint32_t vsize);
+
+ //Represents section expand type for expand_section function
+ enum section_expand_type
+ {
+ expand_section_raw, //Section raw data size will be expanded
+ expand_section_virtual //Section virtual data size will be expanded
+ };
+
+ //Expands section raw or virtual size to hold data from specified RVA with specified size
+ //Section must be free (not bound to any image)
+ //or the last section of this image
+ //Returns true if section was expanded
+ bool expand_section(section& s, uint32_t needed_rva, uint32_t needed_size, section_expand_type expand);
+
+ //Adds section to image
+ //Returns last section
+ section& add_section(section s);
+ //Prepares section to later add it to image (checks and recalculates virtual and raw section size)
+ //Section must be prepared by this function before calling add_section
+ void prepare_section(section& s);
+
+ //Returns true if sectios "s" is already attached to this PE file
+ bool section_attached(const section& s) const;
+
+
+public: //IMAGE
+ //Returns PE type (PE or PE+) from pe_type enumeration (minimal correctness checks)
+ static pe_type get_pe_type(std::istream& file);
+ //Returns PE type of this image
+ pe_type get_pe_type() const;
+
+ //Returns true if image has overlay data at the end of file
+ bool has_overlay() const;
+
+ //Realigns file (changes file alignment)
+ void realign_file(uint32_t new_file_alignment);
+
+ //Helper function to recalculate RAW and virtual section sizes and strip it, if necessary
+ //auto_strip = strip section, if necessary
+ void recalculate_section_sizes(section& s, bool auto_strip);
+
+ // ========== END OF PUBLIC MEMBERS AND STRUCTURES ========== //
+private:
+ //Image DOS header
+ pe_win::image_dos_header dos_header_;
+ //Rich (stub) overlay data (for MSVS)
+ std::string rich_overlay_;
+ //List of image sections
+ section_list sections_;
+ //True if image has overlay
+ bool has_overlay_;
+ //Raw SizeOfHeaders-sized data from the beginning of image
+ std::string full_headers_data_;
+ //Raw debug data for all directories
+ //PointerToRawData; Data
+ debug_data_list debug_data_;
+ //PE or PE+ related properties
+ pe_properties* props_;
+
+ //Reads and checks DOS header
+ void read_dos_header(std::istream& file);
+
+ //Reads and checks PE headers and section headers, data
+ void read_pe(std::istream& file, bool read_debug_raw_data);
+
+ //Sets number of sections
+ void set_number_of_sections(uint16_t number);
+ //Sets size of image
+ void set_size_of_image(uint32_t size);
+ //Sets file alignment (no checks)
+ void set_file_alignment_unchecked(uint32_t alignment);
+ //Returns needed magic of image
+ uint32_t get_needed_magic() const;
+ //Returns nt headers data pointer
+ char* get_nt_headers_ptr();
+
+private:
+ static const uint16_t maximum_number_of_sections = 0x60;
+ static const uint32_t minimum_file_alignment = 512;
+
+private:
+ //RAW file offset to section convertion helpers (4gb max)
+ section_list::const_iterator file_offset_to_section(uint32_t offset) const;
+ section_list::iterator file_offset_to_section(uint32_t offset);
+};
+}
diff --git a/tools/pe_bliss/pe_bliss.h b/tools/pe_bliss/pe_bliss.h
new file mode 100644
index 0000000000..1a8b430284
--- /dev/null
+++ b/tools/pe_bliss/pe_bliss.h
@@ -0,0 +1,39 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include "pe_base.h"
+#include "pe_rebuilder.h"
+#include "pe_factory.h"
+#include "pe_bound_import.h"
+#include "pe_debug.h"
+#include "pe_dotnet.h"
+#include "pe_exception_directory.h"
+#include "pe_exports.h"
+#include "pe_imports.h"
+#include "pe_load_config.h"
+#include "pe_relocations.h"
+#include "pe_resources.h"
+#include "pe_rich_data.h"
+#include "pe_tls.h"
+#include "pe_properties_generic.h"
+#include "pe_checksum.h"
+#include "entropy.h"
diff --git a/tools/pe_bliss/pe_bliss_godot.cpp b/tools/pe_bliss/pe_bliss_godot.cpp
new file mode 100644
index 0000000000..8297aa1045
--- /dev/null
+++ b/tools/pe_bliss/pe_bliss_godot.cpp
@@ -0,0 +1,118 @@
+#include "pe_bliss/pe_bliss.h"
+#include "pe_bliss/pe_bliss_resources.h"
+#include "core/ustring.h"
+#include "core/dvector.h"
+#include "os/file_access.h"
+
+using namespace pe_bliss;
+
+String pe_bliss_add_resrc(const char* p_path, int version_major, int version_minor,
+ String& company_name, String& file_description,
+ String& legal_copyright, String& version_text,
+ String& product_name, String& godot_version,
+ DVector<uint8_t>& icon_content) {
+ try
+ {
+ pe_base image(pe_factory::create_pe(p_path));
+
+ const section_list& pe_sections = image.get_image_sections();
+ uint32_t end_of_pe = 0;
+ FileAccess *dst;
+ DVector<uint8_t> overlay_data;
+ if(image.has_overlay())
+ {
+ end_of_pe = pe_sections.back().get_pointer_to_raw_data() + pe_sections.back().get_size_of_raw_data();
+ dst=FileAccess::open(p_path,FileAccess::READ);
+ if (dst) {
+ overlay_data.resize(dst->get_len()-end_of_pe);
+ dst->seek(end_of_pe);
+ DVector<uint8_t>::Write overlay_data_write = overlay_data.write();
+ dst->get_buffer(overlay_data_write.ptr(),overlay_data.size());
+ dst->close();
+ memdelete(dst);
+ }
+ }
+ resource_directory root;
+ if(image.has_resources())
+ {
+ root = resource_directory(get_resources(image));
+ }
+ pe_resource_manager res(root);
+ if(image.has_resources())
+ {
+ if(icon_content.size()) {
+ if(res.resource_exists(pe_resource_viewer::resource_icon))
+ {
+ res.remove_resource_type(pe_resource_viewer::resource_icon);
+ }
+ if(res.resource_exists(pe_resource_viewer::resource_icon_group))
+ {
+ res.remove_resource_type(pe_resource_viewer::resource_icon_group);
+ }
+ }
+ if(res.resource_exists(pe_resource_viewer::resource_version))
+ {
+ res.remove_resource_type(pe_resource_viewer::resource_version);
+ }
+ }
+ file_version_info file_info;
+ file_info.set_file_os(file_version_info::file_os_nt_win32);
+ file_info.set_file_type(file_version_info::file_type_application);
+ unsigned int ver = version_major << 16;
+ ver = ver + version_minor;
+ file_info.set_file_version_ms(ver);
+ file_info.set_file_version_ls(0x00000000);
+ file_info.set_product_version_ms(ver);
+ file_info.set_product_version_ls(0x00000000);
+ lang_string_values_map strings;
+ translation_values_map translations;
+ version_info_editor version(strings, translations);
+ version.add_translation(version_info_editor::default_language_translation);
+ version.set_company_name(company_name.c_str());
+ version.set_file_description(file_description.c_str());
+ if (!product_name.empty()) {
+ version.set_internal_name((product_name+String(".exe")).c_str());
+ version.set_original_filename((product_name+String(".exe")).c_str());
+ version.set_product_name(product_name.c_str());
+ }
+ version.set_legal_copyright(legal_copyright.c_str());
+ version.set_product_version(version_text.c_str());
+ if(!godot_version.empty()) version.set_property(L"Godot Engine Version", godot_version.c_str() );
+ resource_version_info_writer(res).set_version_info(file_info, strings, translations, 1033, 1200);
+ if(icon_content.size()) {
+ std::string icon;
+ icon.resize(icon_content.size());
+ for(int i=0; i<icon_content.size(); i++)
+ {
+ icon[i] = icon_content[i];
+ }
+ resource_cursor_icon_writer(res).add_icon(icon, L"MAIN_ICON", 1033);
+ }
+ if(image.has_resources())
+ {
+ rebuild_resources(image, root, image.section_from_directory(pe_win::image_directory_entry_resource));
+ } else {
+ section new_resources;
+ new_resources.get_raw_data().resize(1);
+ new_resources.set_name(".rsrc");
+ new_resources.readable(true);
+ section& attached_section = image.add_section(new_resources);
+ rebuild_resources(image, root, attached_section);
+ }
+ rebuild_pe(image, p_path);
+ if(image.has_overlay() && end_of_pe) {
+ dst=FileAccess::open(p_path,FileAccess::READ_WRITE);
+ if (dst) {
+ dst->seek_end();
+ DVector<uint8_t>::Read overlay_data_read = overlay_data.read();
+ dst->store_buffer(overlay_data_read.ptr(),overlay_data.size());
+ dst->close();
+ memdelete(dst);
+ }
+ }
+ return String();
+ } catch(const pe_exception& e) {
+ String ret("Error In Add rsrc Section : ");
+ return ret + String(e.what());
+ }
+}
diff --git a/tools/pe_bliss/pe_bliss_godot.h b/tools/pe_bliss/pe_bliss_godot.h
new file mode 100644
index 0000000000..0365ca9eaf
--- /dev/null
+++ b/tools/pe_bliss/pe_bliss_godot.h
@@ -0,0 +1,7 @@
+
+
+String pe_bliss_add_resrc(const char* p_path, int version_major, int version_minor,
+ String& company_name, String& file_description,
+ String& legal_copyright, String& version_text,
+ String& product_name, String& godot_version,
+ DVector<uint8_t>& icon_content);
diff --git a/tools/pe_bliss/pe_bliss_resources.h b/tools/pe_bliss/pe_bliss_resources.h
new file mode 100644
index 0000000000..60369f8011
--- /dev/null
+++ b/tools/pe_bliss/pe_bliss_resources.h
@@ -0,0 +1,36 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include "file_version_info.h"
+#include "message_table.h"
+#include "pe_resource_manager.h"
+#include "pe_resource_viewer.h"
+#include "version_info_editor.h"
+#include "version_info_viewer.h"
+#include "resource_bitmap_reader.h"
+#include "resource_bitmap_writer.h"
+#include "resource_cursor_icon_reader.h"
+#include "resource_cursor_icon_writer.h"
+#include "resource_version_info_reader.h"
+#include "resource_version_info_writer.h"
+#include "resource_string_table_reader.h"
+#include "resource_message_list_reader.h"
diff --git a/tools/pe_bliss/pe_bound_import.cpp b/tools/pe_bliss/pe_bound_import.cpp
new file mode 100644
index 0000000000..4b54b36105
--- /dev/null
+++ b/tools/pe_bliss/pe_bound_import.cpp
@@ -0,0 +1,311 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <string.h>
+#include "pe_bound_import.h"
+#include "utils.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//BOUND IMPORT
+//Default constructor
+bound_import_ref::bound_import_ref()
+ :timestamp_(0)
+{}
+
+//Constructor from data
+bound_import_ref::bound_import_ref(const std::string& module_name, uint32_t timestamp)
+ :module_name_(module_name), timestamp_(timestamp)
+{}
+
+//Returns imported module name
+const std::string& bound_import_ref::get_module_name() const
+{
+ return module_name_;
+}
+
+//Returns bound import date and time stamp
+uint32_t bound_import_ref::get_timestamp() const
+{
+ return timestamp_;
+}
+
+//Sets module name
+void bound_import_ref::set_module_name(const std::string& module_name)
+{
+ module_name_ = module_name;
+}
+
+//Sets timestamp
+void bound_import_ref::set_timestamp(uint32_t timestamp)
+{
+ timestamp_ = timestamp;
+}
+
+//Default constructor
+bound_import::bound_import()
+ :timestamp_(0)
+{}
+
+//Constructor from data
+bound_import::bound_import(const std::string& module_name, uint32_t timestamp)
+ :module_name_(module_name), timestamp_(timestamp)
+{}
+
+//Returns imported module name
+const std::string& bound_import::get_module_name() const
+{
+ return module_name_;
+}
+
+//Returns bound import date and time stamp
+uint32_t bound_import::get_timestamp() const
+{
+ return timestamp_;
+}
+
+//Returns bound references cound
+size_t bound_import::get_module_ref_count() const
+{
+ return refs_.size();
+}
+
+//Returns module references
+const bound_import::ref_list& bound_import::get_module_ref_list() const
+{
+ return refs_;
+}
+
+//Adds module reference
+void bound_import::add_module_ref(const bound_import_ref& ref)
+{
+ refs_.push_back(ref);
+}
+
+//Clears module references list
+void bound_import::clear_module_refs()
+{
+ refs_.clear();
+}
+
+//Returns module references
+bound_import::ref_list& bound_import::get_module_ref_list()
+{
+ return refs_;
+}
+
+//Sets module name
+void bound_import::set_module_name(const std::string& module_name)
+{
+ module_name_ = module_name;
+}
+
+//Sets timestamp
+void bound_import::set_timestamp(uint32_t timestamp)
+{
+ timestamp_ = timestamp;
+}
+
+const bound_import_module_list get_bound_import_module_list(const pe_base& pe)
+{
+ //Returned bound import modules list
+ bound_import_module_list ret;
+
+ //If image has no bound imports
+ if(!pe.has_bound_import())
+ return ret;
+
+ uint32_t bound_import_data_len =
+ pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true);
+
+ if(bound_import_data_len < pe.get_directory_size(image_directory_entry_bound_import))
+ throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory);
+
+ const char* bound_import_data = pe.section_data_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true);
+
+ //Check read in "read_pe" function raw bound import data size
+ if(bound_import_data_len < sizeof(image_bound_import_descriptor))
+ throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory);
+
+ //current bound_import_data_ in-string position
+ unsigned long current_pos = 0;
+ //first bound import descriptor
+ //so, we're working with raw data here, no section helpers available
+ const image_bound_import_descriptor* descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data[current_pos]);
+
+ //Enumerate until zero
+ while(descriptor->OffsetModuleName)
+ {
+ //Check module name offset
+ if(descriptor->OffsetModuleName >= bound_import_data_len)
+ throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory);
+
+ //Check module name for null-termination
+ if(!pe_utils::is_null_terminated(&bound_import_data[descriptor->OffsetModuleName], bound_import_data_len - descriptor->OffsetModuleName))
+ throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory);
+
+ //Create bound import descriptor structure
+ bound_import elem(&bound_import_data[descriptor->OffsetModuleName], descriptor->TimeDateStamp);
+
+ //Check DWORDs
+ if(descriptor->NumberOfModuleForwarderRefs >= pe_utils::max_dword / sizeof(image_bound_forwarder_ref)
+ || !pe_utils::is_sum_safe(current_pos, 2 /* this descriptor and the next one */ * sizeof(image_bound_import_descriptor) + descriptor->NumberOfModuleForwarderRefs * sizeof(image_bound_forwarder_ref)))
+ throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory);
+
+ //Move after current descriptor
+ current_pos += sizeof(image_bound_import_descriptor);
+
+ //Enumerate referenced bound import descriptors
+ for(unsigned long i = 0; i != descriptor->NumberOfModuleForwarderRefs; ++i)
+ {
+ //They're just after parent descriptor
+ //Check size of structure
+ if(current_pos + sizeof(image_bound_forwarder_ref) > bound_import_data_len)
+ throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory);
+
+ //Get IMAGE_BOUND_FORWARDER_REF pointer
+ const image_bound_forwarder_ref* ref_descriptor = reinterpret_cast<const image_bound_forwarder_ref*>(&bound_import_data[current_pos]);
+
+ //Check referenced module name
+ if(ref_descriptor->OffsetModuleName >= bound_import_data_len)
+ throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory);
+
+ //And its null-termination
+ if(!pe_utils::is_null_terminated(&bound_import_data[ref_descriptor->OffsetModuleName], bound_import_data_len - ref_descriptor->OffsetModuleName))
+ throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory);
+
+ //Add referenced module to current bound import structure
+ elem.add_module_ref(bound_import_ref(&bound_import_data[ref_descriptor->OffsetModuleName], ref_descriptor->TimeDateStamp));
+
+ //Move after referenced bound import descriptor
+ current_pos += sizeof(image_bound_forwarder_ref);
+ }
+
+ //Check structure size
+ if(current_pos + sizeof(image_bound_import_descriptor) > bound_import_data_len)
+ throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory);
+
+ //Move to next bound import descriptor
+ descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data[current_pos]);
+
+ //Save created descriptor structure and references
+ ret.push_back(elem);
+ }
+
+ //Return result
+ return ret;
+}
+
+//imports - bound imported modules list
+//imports_section - section where export directory will be placed (must be attached to PE image)
+//offset_from_section_start - offset from imports_section raw data start
+//save_to_pe_headers - if true, new bound import directory information will be saved to PE image headers
+//auto_strip_last_section - if true and bound imports are placed in the last section, it will be automatically stripped
+const image_directory rebuild_bound_imports(pe_base& pe, const bound_import_module_list& imports, section& imports_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section)
+{
+ //Check that exports_section is attached to this PE image
+ if(!pe.section_attached(imports_section))
+ throw pe_exception("Bound import section must be attached to PE file", pe_exception::section_is_not_attached);
+
+ uint32_t directory_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t));
+ uint32_t needed_size = sizeof(image_bound_import_descriptor) /* Ending null descriptor */;
+ uint32_t needed_size_for_strings = 0;
+
+ //Calculate needed size for bound import data
+ for(bound_import_module_list::const_iterator it = imports.begin(); it != imports.end(); ++it)
+ {
+ const bound_import& import = *it;
+ needed_size += sizeof(image_bound_import_descriptor);
+ needed_size_for_strings += static_cast<uint32_t>((*it).get_module_name().length()) + 1 /* nullbyte */;
+
+ const bound_import::ref_list& refs = import.get_module_ref_list();
+ for(bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it)
+ {
+ needed_size_for_strings += static_cast<uint32_t>((*ref_it).get_module_name().length()) + 1 /* nullbyte */;
+ needed_size += sizeof(image_bound_forwarder_ref);
+ }
+ }
+
+ needed_size += needed_size_for_strings;
+
+ //Check if imports_section is last one. If it's not, check if there's enough place for bound import data
+ if(&imports_section != &*(pe.get_image_sections().end() - 1) &&
+ (imports_section.empty() || pe_utils::align_up(imports_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + directory_pos))
+ throw pe_exception("Insufficient space for bound import directory", pe_exception::insufficient_space);
+
+ std::string& raw_data = imports_section.get_raw_data();
+
+ //This will be done only if imports_section is the last section of image or for section with unaligned raw length of data
+ if(raw_data.length() < needed_size + directory_pos)
+ raw_data.resize(needed_size + directory_pos); //Expand section raw data
+
+ uint32_t current_pos_for_structures = directory_pos;
+ uint32_t current_pos_for_strings = current_pos_for_structures + needed_size - needed_size_for_strings;
+
+ for(bound_import_module_list::const_iterator it = imports.begin(); it != imports.end(); ++it)
+ {
+ const bound_import& import = *it;
+ image_bound_import_descriptor descriptor;
+ descriptor.NumberOfModuleForwarderRefs = static_cast<uint16_t>(import.get_module_ref_list().size());
+ descriptor.OffsetModuleName = static_cast<uint16_t>(current_pos_for_strings - directory_pos);
+ descriptor.TimeDateStamp = import.get_timestamp();
+
+ memcpy(&raw_data[current_pos_for_structures], &descriptor, sizeof(descriptor));
+ current_pos_for_structures += sizeof(descriptor);
+
+ size_t length = import.get_module_name().length() + 1 /* nullbyte */;
+ memcpy(&raw_data[current_pos_for_strings], import.get_module_name().c_str(), length);
+ current_pos_for_strings += static_cast<uint32_t>(length);
+
+ const bound_import::ref_list& refs = import.get_module_ref_list();
+ for(bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it)
+ {
+ const bound_import_ref& ref = *ref_it;
+ image_bound_forwarder_ref ref_descriptor = {0};
+ ref_descriptor.OffsetModuleName = static_cast<uint16_t>(current_pos_for_strings - directory_pos);
+ ref_descriptor.TimeDateStamp = ref.get_timestamp();
+
+ memcpy(&raw_data[current_pos_for_structures], &ref_descriptor, sizeof(ref_descriptor));
+ current_pos_for_structures += sizeof(ref_descriptor);
+
+ length = ref.get_module_name().length() + 1 /* nullbyte */;
+ memcpy(&raw_data[current_pos_for_strings], ref.get_module_name().c_str(), length);
+ current_pos_for_strings += static_cast<uint32_t>(length);
+ }
+ }
+
+ //Adjust section raw and virtual sizes
+ pe.recalculate_section_sizes(imports_section, auto_strip_last_section);
+
+ image_directory ret(pe.rva_from_section_offset(imports_section, directory_pos), needed_size);
+
+ //If auto-rewrite of PE headers is required
+ if(save_to_pe_header)
+ {
+ pe.set_directory_rva(image_directory_entry_bound_import, ret.get_rva());
+ pe.set_directory_size(image_directory_entry_bound_import, ret.get_size());
+ }
+
+ return ret;
+}
+}
diff --git a/tools/pe_bliss/pe_bound_import.h b/tools/pe_bliss/pe_bound_import.h
new file mode 100644
index 0000000000..667e28792e
--- /dev/null
+++ b/tools/pe_bliss/pe_bound_import.h
@@ -0,0 +1,108 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <vector>
+#include <string>
+#include "pe_structures.h"
+#include "pe_base.h"
+#include "pe_directory.h"
+
+namespace pe_bliss
+{
+//Class representing bound import reference
+class bound_import_ref
+{
+public:
+ //Default constructor
+ bound_import_ref();
+ //Constructor from data
+ bound_import_ref(const std::string& module_name, uint32_t timestamp);
+
+ //Returns imported module name
+ const std::string& get_module_name() const;
+ //Returns bound import date and time stamp
+ uint32_t get_timestamp() const;
+
+public: //Setters
+ //Sets module name
+ void set_module_name(const std::string& module_name);
+ //Sets timestamp
+ void set_timestamp(uint32_t timestamp);
+
+private:
+ std::string module_name_; //Imported module name
+ uint32_t timestamp_; //Bound import timestamp
+};
+
+//Class representing image bound import information
+class bound_import
+{
+public:
+ typedef std::vector<bound_import_ref> ref_list;
+
+public:
+ //Default constructor
+ bound_import();
+ //Constructor from data
+ bound_import(const std::string& module_name, uint32_t timestamp);
+
+ //Returns imported module name
+ const std::string& get_module_name() const;
+ //Returns bound import date and time stamp
+ uint32_t get_timestamp() const;
+
+ //Returns bound references cound
+ size_t get_module_ref_count() const;
+ //Returns module references
+ const ref_list& get_module_ref_list() const;
+
+public: //Setters
+ //Sets module name
+ void set_module_name(const std::string& module_name);
+ //Sets timestamp
+ void set_timestamp(uint32_t timestamp);
+
+ //Adds module reference
+ void add_module_ref(const bound_import_ref& ref);
+ //Clears module references list
+ void clear_module_refs();
+ //Returns module references
+ ref_list& get_module_ref_list();
+
+private:
+ std::string module_name_; //Imported module name
+ uint32_t timestamp_; //Bound import timestamp
+ ref_list refs_; //Module references list
+};
+
+typedef std::vector<bound_import> bound_import_module_list;
+
+//Returns bound import information
+const bound_import_module_list get_bound_import_module_list(const pe_base& pe);//Export directory rebuilder
+
+//imports - bound imported modules list
+//imports_section - section where export directory will be placed (must be attached to PE image)
+//offset_from_section_start - offset from imports_section raw data start
+//save_to_pe_headers - if true, new bound import directory information will be saved to PE image headers
+//auto_strip_last_section - if true and bound imports are placed in the last section, it will be automatically stripped
+const image_directory rebuild_bound_imports(pe_base& pe, const bound_import_module_list& imports, section& imports_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true);
+}
diff --git a/tools/pe_bliss/pe_checksum.cpp b/tools/pe_bliss/pe_checksum.cpp
new file mode 100644
index 0000000000..5971a33c90
--- /dev/null
+++ b/tools/pe_bliss/pe_checksum.cpp
@@ -0,0 +1,103 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "pe_checksum.h"
+#include "pe_structures.h"
+#include "pe_base.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//Calculate checksum of image
+uint32_t calculate_checksum(std::istream& file)
+{
+ //Save istream state
+ std::ios_base::iostate state = file.exceptions();
+ std::streamoff old_offset = file.tellg();
+
+ //Checksum value
+ unsigned long long checksum = 0;
+
+ try
+ {
+ image_dos_header header;
+
+ file.exceptions(std::ios::goodbit);
+
+ //Read DOS header
+ pe_base::read_dos_header(file, header);
+
+ //Calculate PE checksum
+ file.seekg(0);
+ unsigned long long top = 0xFFFFFFFF;
+ top++;
+
+ //"CheckSum" field position in optional PE headers - it's always 64 for PE and PE+
+ static const unsigned long checksum_pos_in_optional_headers = 64;
+ //Calculate real PE headers "CheckSum" field position
+ //Sum is safe here
+ unsigned long pe_checksum_pos = header.e_lfanew + sizeof(image_file_header) + sizeof(uint32_t) + checksum_pos_in_optional_headers;
+
+ //Calculate checksum for each byte of file
+ std::streamoff filesize = pe_utils::get_file_size(file);
+ for(long long i = 0; i < filesize; i += 4)
+ {
+ unsigned long dw = 0;
+
+ //Read DWORD from file
+ file.read(reinterpret_cast<char*>(&dw), sizeof(unsigned long));
+ //Skip "CheckSum" DWORD
+ if(i == pe_checksum_pos)
+ continue;
+
+ //Calculate checksum
+ checksum = (checksum & 0xffffffff) + dw + (checksum >> 32);
+ if(checksum > top)
+ checksum = (checksum & 0xffffffff) + (checksum >> 32);
+ }
+
+ //Finish checksum
+ checksum = (checksum & 0xffff) + (checksum >> 16);
+ checksum = (checksum) + (checksum >> 16);
+ checksum = checksum & 0xffff;
+
+ checksum += static_cast<unsigned long>(filesize);
+ }
+ catch(const std::exception&)
+ {
+ //If something went wrong, restore istream state
+ file.exceptions(state);
+ file.seekg(old_offset);
+ file.clear();
+ //Rethrow
+ throw;
+ }
+
+ //Restore istream state
+ file.exceptions(state);
+ file.seekg(old_offset);
+ file.clear();
+
+ //Return checksum
+ return static_cast<uint32_t>(checksum);
+}
+}
diff --git a/tools/pe_bliss/pe_checksum.h b/tools/pe_bliss/pe_checksum.h
new file mode 100644
index 0000000000..a568d5d369
--- /dev/null
+++ b/tools/pe_bliss/pe_checksum.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <istream>
+#include "stdint_defs.h"
+
+namespace pe_bliss
+{
+//Calculate checksum of image (performs no checks on PE structures)
+uint32_t calculate_checksum(std::istream& file);
+}
diff --git a/tools/pe_bliss/pe_debug.cpp b/tools/pe_bliss/pe_debug.cpp
new file mode 100644
index 0000000000..a0ed3f5af1
--- /dev/null
+++ b/tools/pe_bliss/pe_debug.cpp
@@ -0,0 +1,865 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <string.h>
+#include "pe_debug.h"
+#include "utils.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+//DEBUG
+//Default constructor
+debug_info::debug_info()
+ :characteristics_(0),
+ time_stamp_(0),
+ major_version_(0), minor_version_(0),
+ type_(0),
+ size_of_data_(0),
+ address_of_raw_data_(0),
+ pointer_to_raw_data_(0),
+ advanced_info_type_(advanced_info_none)
+{}
+
+//Constructor from data
+debug_info::debug_info(const image_debug_directory& debug)
+ :characteristics_(debug.Characteristics),
+ time_stamp_(debug.TimeDateStamp),
+ major_version_(debug.MajorVersion), minor_version_(debug.MinorVersion),
+ type_(debug.Type),
+ size_of_data_(debug.SizeOfData),
+ address_of_raw_data_(debug.AddressOfRawData),
+ pointer_to_raw_data_(debug.PointerToRawData),
+ advanced_info_type_(advanced_info_none)
+{}
+
+//Returns debug characteristics
+uint32_t debug_info::get_characteristics() const
+{
+ return characteristics_;
+}
+
+//Returns debug datetimestamp
+uint32_t debug_info::get_time_stamp() const
+{
+ return time_stamp_;
+}
+
+//Returns major version
+uint32_t debug_info::get_major_version() const
+{
+ return major_version_;
+}
+
+//Returns minor version
+uint32_t debug_info::get_minor_version() const
+{
+ return minor_version_;
+}
+
+//Returns type of debug info (unchecked)
+uint32_t debug_info::get_type_raw() const
+{
+ return type_;
+}
+
+//Returns type of debug info from debug_info_type enumeration
+debug_info::debug_info_type debug_info::get_type() const
+{
+ //Determine debug type
+ switch(type_)
+ {
+ case image_debug_type_coff:
+ return debug_type_coff;
+
+ case image_debug_type_codeview:
+ return debug_type_codeview;
+
+ case image_debug_type_fpo:
+ return debug_type_fpo;
+
+ case image_debug_type_misc:
+ return debug_type_misc;
+
+ case image_debug_type_exception:
+ return debug_type_exception;
+
+ case image_debug_type_fixup:
+ return debug_type_fixup;
+
+ case image_debug_type_omap_to_src:
+ return debug_type_omap_to_src;
+
+ case image_debug_type_omap_from_src:
+ return debug_type_omap_from_src;
+
+ case image_debug_type_borland:
+ return debug_type_borland;
+
+ case image_debug_type_clsid:
+ return debug_type_clsid;
+
+ case image_debug_type_reserved10:
+ return debug_type_reserved10;
+ }
+
+ return debug_type_unknown;
+}
+
+//Returns size of debug data (internal, .pdb or other file doesn't count)
+uint32_t debug_info::get_size_of_data() const
+{
+ return size_of_data_;
+}
+
+//Returns RVA of debug info when mapped to memory or zero, if info is not mapped
+uint32_t debug_info::get_rva_of_raw_data() const
+{
+ return address_of_raw_data_;
+}
+
+//Returns raw file pointer to raw data
+uint32_t debug_info::get_pointer_to_raw_data() const
+{
+ return pointer_to_raw_data_;
+}
+
+//Copy constructor
+debug_info::debug_info(const debug_info& info)
+ :characteristics_(info.characteristics_),
+ time_stamp_(info.time_stamp_),
+ major_version_(info.major_version_), minor_version_(info.minor_version_),
+ type_(info.type_),
+ size_of_data_(info.size_of_data_),
+ address_of_raw_data_(info.address_of_raw_data_),
+ pointer_to_raw_data_(info.pointer_to_raw_data_),
+ advanced_info_type_(info.advanced_info_type_)
+{
+ copy_advanced_info(info);
+}
+
+//Copy assignment operator
+debug_info& debug_info::operator=(const debug_info& info)
+{
+ copy_advanced_info(info);
+
+ characteristics_ = info.characteristics_;
+ time_stamp_ = info.time_stamp_;
+ major_version_ = info.major_version_;
+ minor_version_ = info.minor_version_;
+ type_ = info.type_;
+ size_of_data_ = info.size_of_data_;
+ address_of_raw_data_ = info.address_of_raw_data_;
+ pointer_to_raw_data_ = info.pointer_to_raw_data_;
+ advanced_info_type_ = info.advanced_info_type_;
+
+ return *this;
+}
+
+//Default constructor
+debug_info::advanced_info::advanced_info()
+ :adv_pdb_7_0_info(0) //Zero pointer to advanced data
+{}
+
+//Returns true if advanced debug info is present
+bool debug_info::advanced_info::is_present() const
+{
+ return adv_pdb_7_0_info != 0;
+}
+
+//Helper for advanced debug information copying
+void debug_info::copy_advanced_info(const debug_info& info)
+{
+ free_present_advanced_info();
+
+ switch(info.advanced_info_type_)
+ {
+ case advanced_info_pdb_7_0:
+ advanced_debug_info_.adv_pdb_7_0_info = new pdb_7_0_info(*info.advanced_debug_info_.adv_pdb_7_0_info);
+ break;
+ case advanced_info_pdb_2_0:
+ advanced_debug_info_.adv_pdb_2_0_info = new pdb_2_0_info(*info.advanced_debug_info_.adv_pdb_2_0_info);
+ break;
+ case advanced_info_misc:
+ advanced_debug_info_.adv_misc_info = new misc_debug_info(*info.advanced_debug_info_.adv_misc_info);
+ break;
+ case advanced_info_coff:
+ advanced_debug_info_.adv_coff_info = new coff_debug_info(*info.advanced_debug_info_.adv_coff_info);
+ break;
+ default:
+ break;
+ }
+
+ advanced_info_type_ = info.advanced_info_type_;
+}
+
+//Helper for clearing any present advanced debug information
+void debug_info::free_present_advanced_info()
+{
+ switch(advanced_info_type_)
+ {
+ case advanced_info_pdb_7_0:
+ delete advanced_debug_info_.adv_pdb_7_0_info;
+ break;
+ case advanced_info_pdb_2_0:
+ delete advanced_debug_info_.adv_pdb_2_0_info;
+ break;
+ case advanced_info_misc:
+ delete advanced_debug_info_.adv_misc_info;
+ break;
+ case advanced_info_coff:
+ delete advanced_debug_info_.adv_coff_info;
+ break;
+ default:
+ break;
+ }
+
+ advanced_debug_info_.adv_pdb_7_0_info = 0;
+ advanced_info_type_ = advanced_info_none;
+}
+
+//Destructor
+debug_info::~debug_info()
+{
+ free_present_advanced_info();
+}
+
+//Sets advanced debug information
+void debug_info::set_advanced_debug_info(const pdb_7_0_info& info)
+{
+ free_present_advanced_info();
+ advanced_debug_info_.adv_pdb_7_0_info = new pdb_7_0_info(info);
+ advanced_info_type_ = advanced_info_pdb_7_0;
+}
+
+void debug_info::set_advanced_debug_info(const pdb_2_0_info& info)
+{
+ free_present_advanced_info();
+ advanced_debug_info_.adv_pdb_2_0_info = new pdb_2_0_info(info);
+ advanced_info_type_ = advanced_info_pdb_2_0;
+}
+
+void debug_info::set_advanced_debug_info(const misc_debug_info& info)
+{
+ free_present_advanced_info();
+ advanced_debug_info_.adv_misc_info = new misc_debug_info(info);
+ advanced_info_type_ = advanced_info_misc;
+}
+
+void debug_info::set_advanced_debug_info(const coff_debug_info& info)
+{
+ free_present_advanced_info();
+ advanced_debug_info_.adv_coff_info = new coff_debug_info(info);
+ advanced_info_type_ = advanced_info_coff;
+}
+
+//Returns advanced debug information type
+debug_info::advanced_info_type debug_info::get_advanced_info_type() const
+{
+ return advanced_info_type_;
+}
+
+//Returns advanced debug information or throws an exception,
+//if requested information type is not contained by structure
+template<>
+const pdb_7_0_info debug_info::get_advanced_debug_info<pdb_7_0_info>() const
+{
+ if(advanced_info_type_ != advanced_info_pdb_7_0)
+ throw pe_exception("Debug info structure does not contain PDB 7.0 data", pe_exception::advanced_debug_information_request_error);
+
+ return *advanced_debug_info_.adv_pdb_7_0_info;
+}
+
+template<>
+const pdb_2_0_info debug_info::get_advanced_debug_info<pdb_2_0_info>() const
+{
+ if(advanced_info_type_ != advanced_info_pdb_2_0)
+ throw pe_exception("Debug info structure does not contain PDB 2.0 data", pe_exception::advanced_debug_information_request_error);
+
+ return *advanced_debug_info_.adv_pdb_2_0_info;
+}
+
+template<>
+const misc_debug_info debug_info::get_advanced_debug_info<misc_debug_info>() const
+{
+ if(advanced_info_type_ != advanced_info_misc)
+ throw pe_exception("Debug info structure does not contain MISC data", pe_exception::advanced_debug_information_request_error);
+
+ return *advanced_debug_info_.adv_misc_info;
+}
+
+template<>
+const coff_debug_info debug_info::get_advanced_debug_info<coff_debug_info>() const
+{
+ if(advanced_info_type_ != advanced_info_coff)
+ throw pe_exception("Debug info structure does not contain COFF data", pe_exception::advanced_debug_information_request_error);
+
+ return *advanced_debug_info_.adv_coff_info;
+}
+
+//Sets advanced debug information type, if no advanced info structure available
+void debug_info::set_advanced_info_type(advanced_info_type type)
+{
+ free_present_advanced_info();
+ if(advanced_info_type_ >= advanced_info_codeview_4_0) //Don't set info type for those types, which have advanced info structures
+ advanced_info_type_ = type;
+}
+
+//Default constructor
+pdb_7_0_info::pdb_7_0_info()
+ :age_(0)
+{
+ memset(&guid_, 0, sizeof(guid_));
+}
+
+//Constructor from data
+pdb_7_0_info::pdb_7_0_info(const CV_INFO_PDB70* info)
+ :age_(info->Age), guid_(info->Signature),
+ pdb_file_name_(reinterpret_cast<const char*>(info->PdbFileName)) //Must be checked before for null-termination
+{}
+
+//Returns debug PDB 7.0 structure GUID
+const guid pdb_7_0_info::get_guid() const
+{
+ return guid_;
+}
+
+//Returns age of build
+uint32_t pdb_7_0_info::get_age() const
+{
+ return age_;
+}
+
+//Returns PDB file name / path
+const std::string& pdb_7_0_info::get_pdb_file_name() const
+{
+ return pdb_file_name_;
+}
+
+//Default constructor
+pdb_2_0_info::pdb_2_0_info()
+ :age_(0), signature_(0)
+{}
+
+//Constructor from data
+pdb_2_0_info::pdb_2_0_info(const CV_INFO_PDB20* info)
+ :age_(info->Age), signature_(info->Signature),
+ pdb_file_name_(reinterpret_cast<const char*>(info->PdbFileName)) //Must be checked before for null-termination
+{}
+
+//Returns debug PDB 2.0 structure signature
+uint32_t pdb_2_0_info::get_signature() const
+{
+ return signature_;
+}
+
+//Returns age of build
+uint32_t pdb_2_0_info::get_age() const
+{
+ return age_;
+}
+
+//Returns PDB file name / path
+const std::string& pdb_2_0_info::get_pdb_file_name() const
+{
+ return pdb_file_name_;
+}
+
+//Default constructor
+misc_debug_info::misc_debug_info()
+ :data_type_(0), unicode_(false)
+{}
+
+//Constructor from data
+misc_debug_info::misc_debug_info(const image_debug_misc* info)
+ :data_type_(info->DataType), unicode_(info->Unicode ? true : false)
+{
+ //IMAGE_DEBUG_MISC::Data must be checked before!
+ if(info->Unicode)
+ {
+#ifdef PE_BLISS_WINDOWS
+ debug_data_unicode_ = std::wstring(reinterpret_cast<const wchar_t*>(info->Data), (info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */) / 2);
+#else
+ debug_data_unicode_ = pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(info->Data), (info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */) / 2));
+#endif
+
+ pe_utils::strip_nullbytes(debug_data_unicode_); //Strip nullbytes in the end of string
+ }
+ else
+ {
+ debug_data_ansi_ = std::string(reinterpret_cast<const char*>(info->Data), info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */);
+ pe_utils::strip_nullbytes(debug_data_ansi_); //Strip nullbytes in the end of string
+ }
+}
+
+//Returns debug data type
+uint32_t misc_debug_info::get_data_type() const
+{
+ return data_type_;
+}
+
+//Returns true if data type is exe name
+bool misc_debug_info::is_exe_name() const
+{
+ return data_type_ == image_debug_misc_exename;
+}
+
+//Returns true if debug data is UNICODE
+bool misc_debug_info::is_unicode() const
+{
+ return unicode_;
+}
+
+//Returns debug data (ANSI)
+const std::string& misc_debug_info::get_data_ansi() const
+{
+ return debug_data_ansi_;
+}
+
+//Returns debug data (UNICODE)
+const std::wstring& misc_debug_info::get_data_unicode() const
+{
+ return debug_data_unicode_;
+}
+
+//Default constructor
+coff_debug_info::coff_debug_info()
+ :number_of_symbols_(0),
+ lva_to_first_symbol_(0),
+ number_of_line_numbers_(0),
+ lva_to_first_line_number_(0),
+ rva_to_first_byte_of_code_(0),
+ rva_to_last_byte_of_code_(0),
+ rva_to_first_byte_of_data_(0),
+ rva_to_last_byte_of_data_(0)
+{}
+
+//Constructor from data
+coff_debug_info::coff_debug_info(const image_coff_symbols_header* info)
+ :number_of_symbols_(info->NumberOfSymbols),
+ lva_to_first_symbol_(info->LvaToFirstSymbol),
+ number_of_line_numbers_(info->NumberOfLinenumbers),
+ lva_to_first_line_number_(info->LvaToFirstLinenumber),
+ rva_to_first_byte_of_code_(info->RvaToFirstByteOfCode),
+ rva_to_last_byte_of_code_(info->RvaToLastByteOfCode),
+ rva_to_first_byte_of_data_(info->RvaToFirstByteOfData),
+ rva_to_last_byte_of_data_(info->RvaToLastByteOfData)
+{}
+
+//Returns number of symbols
+uint32_t coff_debug_info::get_number_of_symbols() const
+{
+ return number_of_symbols_;
+}
+
+//Returns virtual address of the first symbol
+uint32_t coff_debug_info::get_lva_to_first_symbol() const
+{
+ return lva_to_first_symbol_;
+}
+
+//Returns number of line-number entries
+uint32_t coff_debug_info::get_number_of_line_numbers() const
+{
+ return number_of_line_numbers_;
+}
+
+//Returns virtual address of the first line-number entry
+uint32_t coff_debug_info::get_lva_to_first_line_number() const
+{
+ return lva_to_first_line_number_;
+}
+
+//Returns relative virtual address of the first byte of code
+uint32_t coff_debug_info::get_rva_to_first_byte_of_code() const
+{
+ return rva_to_first_byte_of_code_;
+}
+
+//Returns relative virtual address of the last byte of code
+uint32_t coff_debug_info::get_rva_to_last_byte_of_code() const
+{
+ return rva_to_last_byte_of_code_;
+}
+
+//Returns relative virtual address of the first byte of data
+uint32_t coff_debug_info::get_rva_to_first_byte_of_data() const
+{
+ return rva_to_first_byte_of_data_;
+}
+
+//Returns relative virtual address of the last byte of data
+uint32_t coff_debug_info::get_rva_to_last_byte_of_data() const
+{
+ return rva_to_last_byte_of_data_;
+}
+
+//Returns COFF symbols list
+const coff_debug_info::coff_symbols_list& coff_debug_info::get_symbols() const
+{
+ return symbols_;
+}
+
+//Adds COFF symbol
+void coff_debug_info::add_symbol(const coff_symbol& sym)
+{
+ symbols_.push_back(sym);
+}
+
+//Default constructor
+coff_debug_info::coff_symbol::coff_symbol()
+ :storage_class_(0),
+ index_(0),
+ section_number_(0), rva_(0),
+ type_(0),
+ is_filename_(false)
+{}
+
+//Returns storage class
+uint32_t coff_debug_info::coff_symbol::get_storage_class() const
+{
+ return storage_class_;
+}
+
+//Returns symbol index
+uint32_t coff_debug_info::coff_symbol::get_index() const
+{
+ return index_;
+}
+
+//Returns section number
+uint32_t coff_debug_info::coff_symbol::get_section_number() const
+{
+ return section_number_;
+}
+
+//Returns RVA
+uint32_t coff_debug_info::coff_symbol::get_rva() const
+{
+ return rva_;
+}
+
+//Returns true if structure contains file name
+bool coff_debug_info::coff_symbol::is_file() const
+{
+ return is_filename_;
+}
+
+//Returns text data (symbol or file name)
+const std::string& coff_debug_info::coff_symbol::get_symbol() const
+{
+ return name_;
+}
+
+//Sets storage class
+void coff_debug_info::coff_symbol::set_storage_class(uint32_t storage_class)
+{
+ storage_class_ = storage_class;
+}
+
+//Sets symbol index
+void coff_debug_info::coff_symbol::set_index(uint32_t index)
+{
+ index_ = index;
+}
+
+//Sets section number
+void coff_debug_info::coff_symbol::set_section_number(uint32_t section_number)
+{
+ section_number_ = section_number;
+}
+
+//Sets RVA
+void coff_debug_info::coff_symbol::set_rva(uint32_t rva)
+{
+ rva_ = rva;
+}
+
+//Sets file name
+void coff_debug_info::coff_symbol::set_file_name(const std::string& file_name)
+{
+ name_ = file_name;
+ is_filename_ = true;
+}
+
+//Sets symbol name
+void coff_debug_info::coff_symbol::set_symbol_name(const std::string& symbol_name)
+{
+ name_ = symbol_name;
+ is_filename_ = false;
+}
+
+//Returns type
+uint16_t coff_debug_info::coff_symbol::get_type() const
+{
+ return type_;
+}
+
+//Sets type
+void coff_debug_info::coff_symbol::set_type(uint16_t type)
+{
+ type_ = type;
+}
+
+//Returns debug information list
+const debug_info_list get_debug_information(const pe_base& pe)
+{
+ debug_info_list ret;
+
+ //If there's no debug directory, return empty list
+ if(!pe.has_debug())
+ return ret;
+
+ //Check the length in bytes of the section containing debug directory
+ if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_debug), pe.get_directory_rva(image_directory_entry_debug), section_data_virtual, true)
+ < sizeof(image_debug_directory))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ unsigned long current_pos = pe.get_directory_rva(image_directory_entry_debug);
+
+ //First IMAGE_DEBUG_DIRECTORY table
+ image_debug_directory directory = pe.section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true);
+
+ if(!pe_utils::is_sum_safe(pe.get_directory_rva(image_directory_entry_debug), pe.get_directory_size(image_directory_entry_debug)))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ //Iterate over all IMAGE_DEBUG_DIRECTORY directories
+ while(directory.PointerToRawData
+ && current_pos < pe.get_directory_rva(image_directory_entry_debug) + pe.get_directory_size(image_directory_entry_debug))
+ {
+ //Create debug information structure
+ debug_info info(directory);
+
+ //Find raw debug data
+ const pe_base::debug_data_list& debug_datas = pe.get_raw_debug_data_list();
+ pe_base::debug_data_list::const_iterator it = debug_datas.find(directory.PointerToRawData);
+ if(it != debug_datas.end()) //If it exists, we'll do some detailed debug info research
+ {
+ const std::string& debug_data = (*it).second;
+ switch(directory.Type)
+ {
+ case image_debug_type_coff:
+ {
+ //Check data length
+ if(debug_data.length() < sizeof(image_coff_symbols_header))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ //Get coff header structure pointer
+ const image_coff_symbols_header* coff = reinterpret_cast<const image_coff_symbols_header*>(debug_data.data());
+
+ //Check possible overflows
+ if(coff->NumberOfSymbols >= pe_utils::max_dword / sizeof(image_symbol)
+ || !pe_utils::is_sum_safe(coff->NumberOfSymbols * sizeof(image_symbol), coff->LvaToFirstSymbol))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ //Check data length again
+ if(debug_data.length() < coff->NumberOfSymbols * sizeof(image_symbol) + coff->LvaToFirstSymbol)
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ //Create COFF debug info structure
+ coff_debug_info coff_info(coff);
+
+ //Enumerate debug symbols data
+ for(uint32_t i = 0; i < coff->NumberOfSymbols; ++i)
+ {
+ //Safe sum (checked above)
+ const image_symbol* sym = reinterpret_cast<const image_symbol*>(debug_data.data() + i * sizeof(image_symbol) + coff->LvaToFirstSymbol);
+
+ coff_debug_info::coff_symbol symbol;
+ symbol.set_index(i); //Save symbol index
+ symbol.set_storage_class(sym->StorageClass); //Save storage class
+ symbol.set_type(sym->Type); //Save storage class
+
+ //Check data length again
+ if(!pe_utils::is_sum_safe(i, sym->NumberOfAuxSymbols)
+ || (i + sym->NumberOfAuxSymbols) > coff->NumberOfSymbols
+ || debug_data.length() < (i + 1) * sizeof(image_symbol) + coff->LvaToFirstSymbol + sym->NumberOfAuxSymbols * sizeof(image_symbol))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ //If symbol is filename
+ if(sym->StorageClass == image_sym_class_file)
+ {
+ //Save file name, it is situated just after this IMAGE_SYMBOL structure
+ std::string file_name(reinterpret_cast<const char*>(debug_data.data() + (i + 1) * sizeof(image_symbol)), sym->NumberOfAuxSymbols * sizeof(image_symbol));
+ pe_utils::strip_nullbytes(file_name);
+ symbol.set_file_name(file_name);
+
+ //Save symbol info
+ coff_info.add_symbol(symbol);
+
+ //Move to next symbol
+ i += sym->NumberOfAuxSymbols;
+ continue;
+ }
+
+ //Dump some other symbols
+ if(((sym->StorageClass == image_sym_class_static)
+ && (sym->NumberOfAuxSymbols == 0)
+ && (sym->SectionNumber == 1))
+ ||
+ ((sym->StorageClass == image_sym_class_external)
+ && ISFCN(sym->Type)
+ && (sym->SectionNumber > 0))
+ )
+ {
+ //Save RVA and section number
+ symbol.set_section_number(sym->SectionNumber);
+ symbol.set_rva(sym->Value);
+
+ //If symbol has short name
+ if(sym->N.Name.Short)
+ {
+ //Copy and save symbol name
+ char name_buff[9];
+ memcpy(name_buff, sym->N.ShortName, 8);
+ name_buff[8] = '\0';
+ symbol.set_symbol_name(name_buff);
+ }
+ else
+ {
+ //Symbol has long name
+
+ //Check possible overflows
+ if(!pe_utils::is_sum_safe(coff->LvaToFirstSymbol + coff->NumberOfSymbols * sizeof(image_symbol), sym->N.Name.Long))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ //Here we have an offset to the string table
+ uint32_t symbol_offset = coff->LvaToFirstSymbol + coff->NumberOfSymbols * sizeof(image_symbol) + sym->N.Name.Long;
+
+ //Check data length
+ if(debug_data.length() < symbol_offset)
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ //Check symbol name for null-termination
+ if(!pe_utils::is_null_terminated(debug_data.data() + symbol_offset, debug_data.length() - symbol_offset))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ //Save symbol name
+ symbol.set_symbol_name(debug_data.data() + symbol_offset);
+ }
+
+ //Save symbol info
+ coff_info.add_symbol(symbol);
+
+ //Move to next symbol
+ i += sym->NumberOfAuxSymbols;
+ continue;
+ }
+ }
+
+ info.set_advanced_debug_info(coff_info);
+ }
+ break;
+
+ case image_debug_type_codeview:
+ {
+ //Check data length
+ if(debug_data.length() < sizeof(OMFSignature*))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ //Get POMFSignature structure pointer from the very beginning of debug data
+ const OMFSignature* sig = reinterpret_cast<const OMFSignature*>(debug_data.data());
+ if(!memcmp(sig->Signature, "RSDS", 4))
+ {
+ //Signature is "RSDS" - PDB 7.0
+
+ //Check data length
+ if(debug_data.length() < sizeof(CV_INFO_PDB70))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ const CV_INFO_PDB70* pdb_data = reinterpret_cast<const CV_INFO_PDB70*>(debug_data.data());
+
+ //Check PDB file name null-termination
+ if(!pe_utils::is_null_terminated(pdb_data->PdbFileName, debug_data.length() - (sizeof(CV_INFO_PDB70) - 1 /* BYTE of filename in structure */)))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ info.set_advanced_debug_info(pdb_7_0_info(pdb_data));
+ }
+ else if(!memcmp(sig->Signature, "NB10", 4))
+ {
+ //Signature is "NB10" - PDB 2.0
+
+ //Check data length
+ if(debug_data.length() < sizeof(CV_INFO_PDB20))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ const CV_INFO_PDB20* pdb_data = reinterpret_cast<const CV_INFO_PDB20*>(debug_data.data());
+
+ //Check PDB file name null-termination
+ if(!pe_utils::is_null_terminated(pdb_data->PdbFileName, debug_data.length() - (sizeof(CV_INFO_PDB20) - 1 /* BYTE of filename in structure */)))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ info.set_advanced_debug_info(pdb_2_0_info(pdb_data));
+ }
+ else if(!memcmp(sig->Signature, "NB09", 4))
+ {
+ //CodeView 4.0, no structures available
+ info.set_advanced_info_type(debug_info::advanced_info_codeview_4_0);
+ }
+ else if(!memcmp(sig->Signature, "NB11", 4))
+ {
+ //CodeView 5.0, no structures available
+ info.set_advanced_info_type(debug_info::advanced_info_codeview_5_0);
+ }
+ else if(!memcmp(sig->Signature, "NB05", 4))
+ {
+ //Other CodeView, no structures available
+ info.set_advanced_info_type(debug_info::advanced_info_codeview);
+ }
+ }
+
+ break;
+
+ case image_debug_type_misc:
+ {
+ //Check data length
+ if(debug_data.length() < sizeof(image_debug_misc))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ //Get misc structure pointer
+ const image_debug_misc* misc_data = reinterpret_cast<const image_debug_misc*>(debug_data.data());
+
+ //Check misc data length
+ if(debug_data.length() < misc_data->Length /* Total length of record */)
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ //Save advanced information
+ info.set_advanced_debug_info(misc_debug_info(misc_data));
+ }
+ break;
+ }
+ }
+
+ //Save debug information structure
+ ret.push_back(info);
+
+ //Check possible overflow
+ if(!pe_utils::is_sum_safe(current_pos, sizeof(image_debug_directory)))
+ throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory);
+
+ //Go to next debug entry
+ current_pos += sizeof(image_debug_directory);
+ directory = pe.section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true);
+ }
+
+ return ret;
+}
+}
diff --git a/tools/pe_bliss/pe_debug.h b/tools/pe_bliss/pe_debug.h
new file mode 100644
index 0000000000..73a7e6860d
--- /dev/null
+++ b/tools/pe_bliss/pe_debug.h
@@ -0,0 +1,324 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <vector>
+#include "pe_structures.h"
+#include "pe_base.h"
+
+namespace pe_bliss
+{
+//Class representing advanced RSDS (PDB 7.0) information
+class pdb_7_0_info
+{
+public:
+ //Default constructor
+ pdb_7_0_info();
+ //Constructor from data
+ explicit pdb_7_0_info(const pe_win::CV_INFO_PDB70* info);
+
+ //Returns debug PDB 7.0 structure GUID
+ const pe_win::guid get_guid() const;
+ //Returns age of build
+ uint32_t get_age() const;
+ //Returns PDB file name / path
+ const std::string& get_pdb_file_name() const;
+
+private:
+ uint32_t age_;
+ pe_win::guid guid_;
+ std::string pdb_file_name_;
+};
+
+//Class representing advanced NB10 (PDB 2.0) information
+class pdb_2_0_info
+{
+public:
+ //Default constructor
+ pdb_2_0_info();
+ //Constructor from data
+ explicit pdb_2_0_info(const pe_win::CV_INFO_PDB20* info);
+
+ //Returns debug PDB 2.0 structure signature
+ uint32_t get_signature() const;
+ //Returns age of build
+ uint32_t get_age() const;
+ //Returns PDB file name / path
+ const std::string& get_pdb_file_name() const;
+
+private:
+ uint32_t age_;
+ uint32_t signature_;
+ std::string pdb_file_name_;
+};
+
+//Class representing advanced misc (IMAGE_DEBUG_TYPE_MISC) info
+class misc_debug_info
+{
+public:
+ //Default constructor
+ misc_debug_info();
+ //Constructor from data
+ explicit misc_debug_info(const pe_win::image_debug_misc* info);
+
+ //Returns debug data type
+ uint32_t get_data_type() const;
+ //Returns true if data type is exe name
+ bool is_exe_name() const;
+
+ //Returns true if debug data is UNICODE
+ bool is_unicode() const;
+ //Returns debug data (ANSI or UNICODE)
+ const std::string& get_data_ansi() const;
+ const std::wstring& get_data_unicode() const;
+
+private:
+ uint32_t data_type_;
+ bool unicode_;
+ std::string debug_data_ansi_;
+ std::wstring debug_data_unicode_;
+};
+
+//Class representing COFF (IMAGE_DEBUG_TYPE_COFF) debug info
+class coff_debug_info
+{
+public:
+ //Structure representing COFF symbol
+ struct coff_symbol
+ {
+ public:
+ //Default constructor
+ coff_symbol();
+
+ //Returns storage class
+ uint32_t get_storage_class() const;
+ //Returns symbol index
+ uint32_t get_index() const;
+ //Returns section number
+ uint32_t get_section_number() const;
+ //Returns RVA
+ uint32_t get_rva() const;
+ //Returns type
+ uint16_t get_type() const;
+
+ //Returns true if structure contains file name
+ bool is_file() const;
+ //Returns text data (symbol or file name)
+ const std::string& get_symbol() const;
+
+ public: //These functions do not change everything inside image, they are used by PE class
+ //Sets storage class
+ void set_storage_class(uint32_t storage_class);
+ //Sets symbol index
+ void set_index(uint32_t index);
+ //Sets section number
+ void set_section_number(uint32_t section_number);
+ //Sets RVA
+ void set_rva(uint32_t rva);
+ //Sets type
+ void set_type(uint16_t type);
+
+ //Sets file name
+ void set_file_name(const std::string& file_name);
+ //Sets symbol name
+ void set_symbol_name(const std::string& symbol_name);
+
+ private:
+ uint32_t storage_class_;
+ uint32_t index_;
+ uint32_t section_number_, rva_;
+ uint16_t type_;
+ bool is_filename_;
+ std::string name_;
+ };
+
+public:
+ typedef std::vector<coff_symbol> coff_symbols_list;
+
+public:
+ //Default constructor
+ coff_debug_info();
+ //Constructor from data
+ explicit coff_debug_info(const pe_win::image_coff_symbols_header* info);
+
+ //Returns number of symbols
+ uint32_t get_number_of_symbols() const;
+ //Returns virtual address of the first symbol
+ uint32_t get_lva_to_first_symbol() const;
+ //Returns number of line-number entries
+ uint32_t get_number_of_line_numbers() const;
+ //Returns virtual address of the first line-number entry
+ uint32_t get_lva_to_first_line_number() const;
+ //Returns relative virtual address of the first byte of code
+ uint32_t get_rva_to_first_byte_of_code() const;
+ //Returns relative virtual address of the last byte of code
+ uint32_t get_rva_to_last_byte_of_code() const;
+ //Returns relative virtual address of the first byte of data
+ uint32_t get_rva_to_first_byte_of_data() const;
+ //Returns relative virtual address of the last byte of data
+ uint32_t get_rva_to_last_byte_of_data() const;
+
+ //Returns COFF symbols list
+ const coff_symbols_list& get_symbols() const;
+
+public: //These functions do not change everything inside image, they are used by PE class
+ //Adds COFF symbol
+ void add_symbol(const coff_symbol& sym);
+
+private:
+ uint32_t number_of_symbols_;
+ uint32_t lva_to_first_symbol_;
+ uint32_t number_of_line_numbers_;
+ uint32_t lva_to_first_line_number_;
+ uint32_t rva_to_first_byte_of_code_;
+ uint32_t rva_to_last_byte_of_code_;
+ uint32_t rva_to_first_byte_of_data_;
+ uint32_t rva_to_last_byte_of_data_;
+
+private:
+ coff_symbols_list symbols_;
+};
+
+//Class representing debug information
+class debug_info
+{
+public:
+ //Enumeration of debug information types
+ enum debug_info_type
+ {
+ debug_type_unknown,
+ debug_type_coff,
+ debug_type_codeview,
+ debug_type_fpo,
+ debug_type_misc,
+ debug_type_exception,
+ debug_type_fixup,
+ debug_type_omap_to_src,
+ debug_type_omap_from_src,
+ debug_type_borland,
+ debug_type_reserved10,
+ debug_type_clsid
+ };
+
+public:
+ //Enumeration of advanced debug information types
+ enum advanced_info_type
+ {
+ advanced_info_none, //No advanced info
+ advanced_info_pdb_7_0, //PDB 7.0
+ advanced_info_pdb_2_0, //PDB 2.0
+ advanced_info_misc, //MISC debug info
+ advanced_info_coff, //COFF debug info
+ //No advanced info structures available for types below
+ advanced_info_codeview_4_0, //CodeView 4.0
+ advanced_info_codeview_5_0, //CodeView 5.0
+ advanced_info_codeview //CodeView
+ };
+
+public:
+ //Default constructor
+ debug_info();
+ //Constructor from data
+ explicit debug_info(const pe_win::image_debug_directory& debug);
+ //Copy constructor
+ debug_info(const debug_info& info);
+ //Copy assignment operator
+ debug_info& operator=(const debug_info& info);
+ //Destructor
+ ~debug_info();
+
+ //Returns debug characteristics
+ uint32_t get_characteristics() const;
+ //Returns debug datetimestamp
+ uint32_t get_time_stamp() const;
+ //Returns major version
+ uint32_t get_major_version() const;
+ //Returns minor version
+ uint32_t get_minor_version() const;
+ //Returns type of debug info (unchecked)
+ uint32_t get_type_raw() const;
+ //Returns type of debug info from debug_info_type enumeration
+ debug_info_type get_type() const;
+ //Returns size of debug data (internal, .pdb or other file doesn't count)
+ uint32_t get_size_of_data() const;
+ //Returns RVA of debug info when mapped to memory or zero, if info is not mapped
+ uint32_t get_rva_of_raw_data() const;
+ //Returns raw file pointer to raw data
+ uint32_t get_pointer_to_raw_data() const;
+
+ //Returns advanced debug information type
+ advanced_info_type get_advanced_info_type() const;
+ //Returns advanced debug information or throws an exception,
+ //if requested information type is not contained by structure
+ template<typename AdvancedInfo>
+ const AdvancedInfo get_advanced_debug_info() const;
+
+public: //These functions do not change everything inside image, they are used by PE class
+ //Sets advanced debug information
+ void set_advanced_debug_info(const pdb_7_0_info& info);
+ void set_advanced_debug_info(const pdb_2_0_info& info);
+ void set_advanced_debug_info(const misc_debug_info& info);
+ void set_advanced_debug_info(const coff_debug_info& info);
+
+ //Sets advanced debug information type, if no advanced info structure available
+ void set_advanced_info_type(advanced_info_type type);
+
+private:
+ uint32_t characteristics_;
+ uint32_t time_stamp_;
+ uint32_t major_version_, minor_version_;
+ uint32_t type_;
+ uint32_t size_of_data_;
+ uint32_t address_of_raw_data_; //RVA when mapped or 0
+ uint32_t pointer_to_raw_data_; //RAW file offset
+
+ //Union containing advanced debug information pointer
+ union advanced_info
+ {
+ public:
+ //Default constructor
+ advanced_info();
+
+ //Returns true if advanced debug info is present
+ bool is_present() const;
+
+ public:
+ pdb_7_0_info* adv_pdb_7_0_info;
+ pdb_2_0_info* adv_pdb_2_0_info;
+ misc_debug_info* adv_misc_info;
+ coff_debug_info* adv_coff_info;
+ };
+
+ //Helper for advanced debug information copying
+ void copy_advanced_info(const debug_info& info);
+ //Helper for clearing any present advanced debug information
+ void free_present_advanced_info();
+
+ advanced_info advanced_debug_info_;
+ //Advanced information type
+ advanced_info_type advanced_info_type_;
+};
+
+typedef std::vector<debug_info> debug_info_list;
+
+//Returns debug information list
+const debug_info_list get_debug_information(const pe_base& pe);
+}
diff --git a/tools/pe_bliss/pe_directory.cpp b/tools/pe_bliss/pe_directory.cpp
new file mode 100644
index 0000000000..13ad2afc5d
--- /dev/null
+++ b/tools/pe_bliss/pe_directory.cpp
@@ -0,0 +1,59 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "pe_directory.h"
+
+namespace pe_bliss
+{
+//Default constructor
+image_directory::image_directory()
+ :rva_(0), size_(0)
+{}
+
+//Constructor from data
+image_directory::image_directory(uint32_t rva, uint32_t size)
+ :rva_(rva), size_(size)
+{}
+
+//Returns RVA
+uint32_t image_directory::get_rva() const
+{
+ return rva_;
+}
+
+//Returns size
+uint32_t image_directory::get_size() const
+{
+ return size_;
+}
+
+//Sets RVA
+void image_directory::set_rva(uint32_t rva)
+{
+ rva_ = rva;
+}
+
+//Sets size
+void image_directory::set_size(uint32_t size)
+{
+ size_ = size;
+}
+}
diff --git a/tools/pe_bliss/pe_directory.h b/tools/pe_bliss/pe_directory.h
new file mode 100644
index 0000000000..a7b1ea7a5f
--- /dev/null
+++ b/tools/pe_bliss/pe_directory.h
@@ -0,0 +1,50 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include "stdint_defs.h"
+
+namespace pe_bliss
+{
+//Class representing image directory data
+class image_directory
+{
+public:
+ //Default constructor
+ image_directory();
+ //Constructor from data
+ image_directory(uint32_t rva, uint32_t size);
+
+ //Returns RVA
+ uint32_t get_rva() const;
+ //Returns size
+ uint32_t get_size() const;
+
+ //Sets RVA
+ void set_rva(uint32_t rva);
+ //Sets size
+ void set_size(uint32_t size);
+
+private:
+ uint32_t rva_;
+ uint32_t size_;
+};
+}
diff --git a/tools/pe_bliss/pe_dotnet.cpp b/tools/pe_bliss/pe_dotnet.cpp
new file mode 100644
index 0000000000..f34a76eae8
--- /dev/null
+++ b/tools/pe_bliss/pe_dotnet.cpp
@@ -0,0 +1,186 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <string.h>
+#include "pe_dotnet.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//.NET
+basic_dotnet_info::basic_dotnet_info()
+{
+ memset(&header_, 0, sizeof(header_));
+}
+
+//Constructor from data
+basic_dotnet_info::basic_dotnet_info(const image_cor20_header& header)
+ :header_(header)
+{}
+
+//Returns major runtime version
+uint16_t basic_dotnet_info::get_major_runtime_version() const
+{
+ return header_.MajorRuntimeVersion;
+}
+
+//Returns minor runtime version
+uint16_t basic_dotnet_info::get_minor_runtime_version() const
+{
+ return header_.MinorRuntimeVersion;
+}
+
+//Returns RVA of metadata (symbol table and startup information)
+uint32_t basic_dotnet_info::get_rva_of_metadata() const
+{
+ return header_.MetaData.VirtualAddress;
+}
+
+//Returns size of metadata (symbol table and startup information)
+uint32_t basic_dotnet_info::get_size_of_metadata() const
+{
+ return header_.MetaData.Size;
+}
+
+//Returns flags
+uint32_t basic_dotnet_info::get_flags() const
+{
+ return header_.Flags;
+}
+
+//Returns true if entry point is native
+bool basic_dotnet_info::is_native_entry_point() const
+{
+ return (header_.Flags & comimage_flags_native_entrypoint) ? true : false;
+}
+
+//Returns true if 32 bit required
+bool basic_dotnet_info::is_32bit_required() const
+{
+ return (header_.Flags & comimage_flags_32bitrequired) ? true : false;
+}
+
+//Returns true if image is IL library
+bool basic_dotnet_info::is_il_library() const
+{
+ return (header_.Flags & comimage_flags_il_library) ? true : false;
+}
+
+//Returns true if image uses IL only
+bool basic_dotnet_info::is_il_only() const
+{
+ return (header_.Flags & comimage_flags_ilonly) ? true : false;
+}
+
+//Returns entry point RVA (if entry point is native)
+//Returns entry point managed token (if entry point is managed)
+uint32_t basic_dotnet_info::get_entry_point_rva_or_token() const
+{
+ return header_.EntryPointToken;
+}
+
+//Returns RVA of managed resources
+uint32_t basic_dotnet_info::get_rva_of_resources() const
+{
+ return header_.Resources.VirtualAddress;
+}
+
+//Returns size of managed resources
+uint32_t basic_dotnet_info::get_size_of_resources() const
+{
+ return header_.Resources.Size;
+}
+
+//Returns RVA of strong name signature
+uint32_t basic_dotnet_info::get_rva_of_strong_name_signature() const
+{
+ return header_.StrongNameSignature.VirtualAddress;
+}
+
+//Returns size of strong name signature
+uint32_t basic_dotnet_info::get_size_of_strong_name_signature() const
+{
+ return header_.StrongNameSignature.Size;
+}
+
+//Returns RVA of code manager table
+uint32_t basic_dotnet_info::get_rva_of_code_manager_table() const
+{
+ return header_.CodeManagerTable.VirtualAddress;
+}
+
+//Returns size of code manager table
+uint32_t basic_dotnet_info::get_size_of_code_manager_table() const
+{
+ return header_.CodeManagerTable.Size;
+}
+
+//Returns RVA of VTable fixups
+uint32_t basic_dotnet_info::get_rva_of_vtable_fixups() const
+{
+ return header_.VTableFixups.VirtualAddress;
+}
+
+//Returns size of VTable fixups
+uint32_t basic_dotnet_info::get_size_of_vtable_fixups() const
+{
+ return header_.VTableFixups.Size;
+}
+
+//Returns RVA of export address table jumps
+uint32_t basic_dotnet_info::get_rva_of_export_address_table_jumps() const
+{
+ return header_.ExportAddressTableJumps.VirtualAddress;
+}
+
+//Returns size of export address table jumps
+uint32_t basic_dotnet_info::get_size_of_export_address_table_jumps() const
+{
+ return header_.ExportAddressTableJumps.Size;
+}
+
+//Returns RVA of managed native header
+//(precompiled header info, usually set to zero, for internal use)
+uint32_t basic_dotnet_info::get_rva_of_managed_native_header() const
+{
+ return header_.ManagedNativeHeader.VirtualAddress;
+}
+
+//Returns size of managed native header
+//(precompiled header info, usually set to zero, for internal use)
+uint32_t basic_dotnet_info::get_size_of_managed_native_header() const
+{
+ return header_.ManagedNativeHeader.Size;
+}
+
+//Returns basic .NET information
+//If image is not native, throws an exception
+const basic_dotnet_info get_basic_dotnet_info(const pe_base& pe)
+{
+ //If there's no debug directory, return empty list
+ if(!pe.is_dotnet())
+ throw pe_exception("Image does not have managed code", pe_exception::image_does_not_have_managed_code);
+
+ //Return basic .NET information
+ return basic_dotnet_info(pe.section_data_from_rva<image_cor20_header>(pe.get_directory_rva(image_directory_entry_com_descriptor), section_data_virtual, true));
+}
+}
diff --git a/tools/pe_bliss/pe_dotnet.h b/tools/pe_bliss/pe_dotnet.h
new file mode 100644
index 0000000000..96b0ac7d0a
--- /dev/null
+++ b/tools/pe_bliss/pe_dotnet.h
@@ -0,0 +1,97 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include "pe_structures.h"
+#include "pe_base.h"
+
+namespace pe_bliss
+{
+//Class representing basic .NET header information
+class basic_dotnet_info
+{
+public:
+ //Default constructor
+ basic_dotnet_info();
+ //Constructor from data
+ explicit basic_dotnet_info(const pe_win::image_cor20_header& header);
+
+ //Returns major runtime version
+ uint16_t get_major_runtime_version() const;
+ //Returns minor runtime version
+ uint16_t get_minor_runtime_version() const;
+
+ //Returns RVA of metadata (symbol table and startup information)
+ uint32_t get_rva_of_metadata() const;
+ //Returns size of metadata (symbol table and startup information)
+ uint32_t get_size_of_metadata() const;
+
+ //Returns flags
+ uint32_t get_flags() const;
+
+ //Returns true if entry point is native
+ bool is_native_entry_point() const;
+ //Returns true if 32 bit required
+ bool is_32bit_required() const;
+ //Returns true if image is IL library
+ bool is_il_library() const;
+ //Returns true if image uses IL only
+ bool is_il_only() const;
+
+ //Returns entry point RVA (if entry point is native)
+ //Returns entry point managed token (if entry point is managed)
+ uint32_t get_entry_point_rva_or_token() const;
+
+ //Returns RVA of managed resources
+ uint32_t get_rva_of_resources() const;
+ //Returns size of managed resources
+ uint32_t get_size_of_resources() const;
+ //Returns RVA of strong name signature
+ uint32_t get_rva_of_strong_name_signature() const;
+ //Returns size of strong name signature
+ uint32_t get_size_of_strong_name_signature() const;
+ //Returns RVA of code manager table
+ uint32_t get_rva_of_code_manager_table() const;
+ //Returns size of code manager table
+ uint32_t get_size_of_code_manager_table() const;
+ //Returns RVA of VTable fixups
+ uint32_t get_rva_of_vtable_fixups() const;
+ //Returns size of VTable fixups
+ uint32_t get_size_of_vtable_fixups() const;
+ //Returns RVA of export address table jumps
+ uint32_t get_rva_of_export_address_table_jumps() const;
+ //Returns size of export address table jumps
+ uint32_t get_size_of_export_address_table_jumps() const;
+ //Returns RVA of managed native header
+ //(precompiled header info, usually set to zero, for internal use)
+ uint32_t get_rva_of_managed_native_header() const;
+ //Returns size of managed native header
+ //(precompiled header info, usually set to zero, for internal use)
+ uint32_t get_size_of_managed_native_header() const;
+
+private:
+ pe_win::image_cor20_header header_;
+};
+
+//Returns basic .NET information
+//If image is not native, throws an exception
+const basic_dotnet_info get_basic_dotnet_info(const pe_base& pe);
+}
diff --git a/tools/pe_bliss/pe_exception.cpp b/tools/pe_bliss/pe_exception.cpp
new file mode 100644
index 0000000000..3161f93599
--- /dev/null
+++ b/tools/pe_bliss/pe_exception.cpp
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "pe_exception.h"
+
+namespace pe_bliss
+{
+//PE exception class constructors
+pe_exception::pe_exception(const char* text, exception_id id)
+ :std::runtime_error(text), id_(id)
+{}
+
+pe_exception::pe_exception(const std::string& text, exception_id id)
+ :std::runtime_error(text), id_(id)
+{}
+
+//Returns exception ID
+pe_exception::exception_id pe_exception::get_id() const
+{
+ return id_;
+}
+}
diff --git a/tools/pe_bliss/pe_exception.h b/tools/pe_bliss/pe_exception.h
new file mode 100644
index 0000000000..2b58a95772
--- /dev/null
+++ b/tools/pe_bliss/pe_exception.h
@@ -0,0 +1,130 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <exception>
+#include <stdexcept>
+
+namespace pe_bliss
+{
+//PE exception class
+class pe_exception : public std::runtime_error
+{
+public:
+ //Exception IDs
+ enum exception_id
+ {
+ unknown_error,
+ bad_pe_file,
+ bad_dos_header,
+ image_nt_headers_not_found,
+ error_reading_image_nt_headers,
+ error_reading_data_directories,
+ error_reading_file,
+ pe_signature_incorrect,
+ incorrect_number_of_rva_and_sizes,
+ error_changing_section_virtual_size,
+ section_number_incorrect,
+ section_table_incorrect,
+ incorrect_section_alignment,
+ incorrect_file_alignment,
+ incorrect_size_of_image,
+ incorrect_size_of_headers,
+ image_section_headers_not_found,
+ zero_section_sizes,
+ section_incorrect_addr_or_size,
+ section_not_found,
+ image_section_data_not_found,
+ no_section_found,
+ image_section_table_incorrect,
+ directory_does_not_exist,
+ rva_not_exists,
+ error_reading_section_header,
+ error_reading_overlay,
+ incorrect_address_conversion,
+
+ incorrect_export_directory,
+ incorrect_import_directory,
+ incorrect_relocation_directory,
+ incorrect_tls_directory,
+ incorrect_config_directory,
+ incorrect_bound_import_directory,
+ incorrect_resource_directory,
+ incorrect_exception_directory,
+ incorrect_debug_directory,
+
+ resource_directory_entry_error,
+ resource_directory_entry_not_found,
+ resource_data_entry_not_found,
+ resource_incorrect_bitmap,
+ resource_incorrect_icon,
+ resource_incorrect_cursor,
+ resource_incorrect_string_table,
+ resource_string_not_found,
+ resource_incorrect_message_table,
+ resource_incorrect_version_info,
+
+ advanced_debug_information_request_error,
+ image_does_not_have_managed_code,
+
+ section_is_empty,
+ data_is_empty,
+ stream_is_bad,
+
+ section_is_not_attached,
+ insufficient_space,
+
+ cannot_rebase_relocations,
+
+ exports_list_is_empty,
+ duplicate_exported_function_ordinal,
+ duplicate_exported_function_name,
+
+ version_info_string_does_not_exist,
+
+ no_more_sections_can_be_added,
+
+ no_icon_group_found,
+ no_cursor_group_found,
+
+ encoding_convertion_error,
+
+ error_expanding_section,
+
+ cannot_rebuild_image
+ };
+
+public:
+ //Class constructors
+ explicit pe_exception(const char* text, exception_id id = unknown_error);
+ explicit pe_exception(const std::string& text, exception_id id = unknown_error);
+
+ //Returns exception ID from exception_id enumeration
+ exception_id get_id() const;
+
+ //Destructor
+ virtual ~pe_exception() throw()
+ {}
+
+private:
+ exception_id id_;
+};
+}
diff --git a/tools/pe_bliss/pe_exception_directory.cpp b/tools/pe_bliss/pe_exception_directory.cpp
new file mode 100644
index 0000000000..1813f02021
--- /dev/null
+++ b/tools/pe_bliss/pe_exception_directory.cpp
@@ -0,0 +1,177 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "pe_exception_directory.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//EXCEPTION DIRECTORY (exists on PE+ only)
+//Default constructor
+exception_entry::exception_entry()
+ :begin_address_(0), end_address_(0), unwind_info_address_(0),
+ unwind_info_version_(0),
+ flags_(0),
+ size_of_prolog_(0),
+ count_of_codes_(0),
+ frame_register_(0),
+ frame_offset_(0)
+{}
+
+//Constructor from data
+exception_entry::exception_entry(const image_runtime_function_entry& entry, const unwind_info& unwind_info)
+ :begin_address_(entry.BeginAddress), end_address_(entry.EndAddress), unwind_info_address_(entry.UnwindInfoAddress),
+ unwind_info_version_(unwind_info.Version),
+ flags_(unwind_info.Flags),
+ size_of_prolog_(unwind_info.SizeOfProlog),
+ count_of_codes_(unwind_info.CountOfCodes),
+ frame_register_(unwind_info.FrameRegister),
+ frame_offset_(unwind_info.FrameOffset)
+{}
+
+//Returns starting address of function, affected by exception unwinding
+uint32_t exception_entry::get_begin_address() const
+{
+ return begin_address_;
+}
+
+//Returns ending address of function, affected by exception unwinding
+uint32_t exception_entry::get_end_address() const
+{
+ return end_address_;
+}
+
+//Returns unwind info address
+uint32_t exception_entry::get_unwind_info_address() const
+{
+ return unwind_info_address_;
+}
+
+//Returns UNWIND_INFO structure version
+uint8_t exception_entry::get_unwind_info_version() const
+{
+ return unwind_info_version_;
+}
+
+//Returns unwind info flags
+uint8_t exception_entry::get_flags() const
+{
+ return flags_;
+}
+
+//The function has an exception handler that should be called
+//when looking for functions that need to examine exceptions
+bool exception_entry::has_exception_handler() const
+{
+ return (flags_ & unw_flag_ehandler) ? true : false;
+}
+
+//The function has a termination handler that should be called
+//when unwinding an exception
+bool exception_entry::has_termination_handler() const
+{
+ return (flags_ & unw_flag_uhandler) ? true : false;
+}
+
+//The unwind info structure is not the primary one for the procedure
+bool exception_entry::is_chaininfo() const
+{
+ return (flags_ & unw_flag_chaininfo) ? true : false;
+}
+
+//Returns size of function prolog
+uint8_t exception_entry::get_size_of_prolog() const
+{
+ return size_of_prolog_;
+}
+
+//Returns number of unwind slots
+uint8_t exception_entry::get_number_of_unwind_slots() const
+{
+ return count_of_codes_;
+}
+
+//If the function uses frame pointer
+bool exception_entry::uses_frame_pointer() const
+{
+ return frame_register_ != 0;
+}
+
+//Number of the nonvolatile register used as the frame pointer,
+//using the same encoding for the operation info field of UNWIND_CODE nodes
+uint8_t exception_entry::get_frame_pointer_register_number() const
+{
+ return frame_register_;
+}
+
+//The scaled offset from RSP that is applied to the FP reg when it is established.
+//The actual FP reg is set to RSP + 16 * this number, allowing offsets from 0 to 240
+uint8_t exception_entry::get_scaled_rsp_offset() const
+{
+ return frame_offset_;
+}
+
+//Returns exception directory data (exists on PE+ only)
+//Unwind opcodes are not listed, because their format and list are subject to change
+const exception_entry_list get_exception_directory_data(const pe_base& pe)
+{
+ exception_entry_list ret;
+
+ //If image doesn't have exception directory, return empty list
+ if(!pe.has_exception_directory())
+ return ret;
+
+ //Check the length in bytes of the section containing exception directory
+ if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_exception), pe.get_directory_rva(image_directory_entry_exception), section_data_virtual, true)
+ < sizeof(image_runtime_function_entry))
+ throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory);
+
+ unsigned long current_pos = pe.get_directory_rva(image_directory_entry_exception);
+
+ //Check if structures are DWORD-aligned
+ if(current_pos % sizeof(uint32_t))
+ throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory);
+
+ //First IMAGE_RUNTIME_FUNCTION_ENTRY table
+ image_runtime_function_entry exception_table = pe.section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true);
+
+ //todo: virtual addresses BeginAddress and EndAddress are not checked to be inside image
+ while(exception_table.BeginAddress)
+ {
+ //Check addresses
+ if(exception_table.BeginAddress > exception_table.EndAddress)
+ throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory);
+
+ //Get unwind information
+ unwind_info info = pe.section_data_from_rva<unwind_info>(exception_table.UnwindInfoAddress, section_data_virtual, true);
+
+ //Create exception entry and save it
+ ret.push_back(exception_entry(exception_table, info));
+
+ //Go to next exception entry
+ current_pos += sizeof(image_runtime_function_entry);
+ exception_table = pe.section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true);
+ }
+
+ return ret;
+}
+}
diff --git a/tools/pe_bliss/pe_exception_directory.h b/tools/pe_bliss/pe_exception_directory.h
new file mode 100644
index 0000000000..6f4fc2298b
--- /dev/null
+++ b/tools/pe_bliss/pe_exception_directory.h
@@ -0,0 +1,88 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <vector>
+#include "pe_structures.h"
+#include "pe_base.h"
+
+namespace pe_bliss
+{
+//Class representing exception directory entry
+class exception_entry
+{
+public:
+ //Default constructor
+ exception_entry();
+ //Constructor from data
+ exception_entry(const pe_win::image_runtime_function_entry& entry, const pe_win::unwind_info& unwind_info);
+
+ //Returns starting address of function, affected by exception unwinding
+ uint32_t get_begin_address() const;
+ //Returns ending address of function, affected by exception unwinding
+ uint32_t get_end_address() const;
+ //Returns unwind info address
+ uint32_t get_unwind_info_address() const;
+
+ //Returns UNWIND_INFO structure version
+ uint8_t get_unwind_info_version() const;
+
+ //Returns unwind info flags
+ uint8_t get_flags() const;
+ //The function has an exception handler that should be called
+ //when looking for functions that need to examine exceptions
+ bool has_exception_handler() const;
+ //The function has a termination handler that should be called
+ //when unwinding an exception
+ bool has_termination_handler() const;
+ //The unwind info structure is not the primary one for the procedure
+ bool is_chaininfo() const;
+
+ //Returns size of function prolog
+ uint8_t get_size_of_prolog() const;
+
+ //Returns number of unwind slots
+ uint8_t get_number_of_unwind_slots() const;
+
+ //If the function uses frame pointer
+ bool uses_frame_pointer() const;
+ //Number of the nonvolatile register used as the frame pointer,
+ //using the same encoding for the operation info field of UNWIND_CODE nodes
+ uint8_t get_frame_pointer_register_number() const;
+ //The scaled offset from RSP that is applied to the FP reg when it is established.
+ //The actual FP reg is set to RSP + 16 * this number, allowing offsets from 0 to 240
+ uint8_t get_scaled_rsp_offset() const;
+
+private:
+ uint32_t begin_address_, end_address_, unwind_info_address_;
+ uint8_t unwind_info_version_;
+ uint8_t flags_;
+ uint8_t size_of_prolog_;
+ uint8_t count_of_codes_;
+ uint8_t frame_register_, frame_offset_;
+};
+
+typedef std::vector<exception_entry> exception_entry_list;
+
+//Returns exception directory data (exists on PE+ only)
+//Unwind opcodes are not listed, because their format and list are subject to change
+const exception_entry_list get_exception_directory_data(const pe_base& pe);
+}
diff --git a/tools/pe_bliss/pe_exports.cpp b/tools/pe_bliss/pe_exports.cpp
new file mode 100644
index 0000000000..c2ad895554
--- /dev/null
+++ b/tools/pe_bliss/pe_exports.cpp
@@ -0,0 +1,700 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <set>
+#include <algorithm>
+#include <string.h>
+#include "pe_exports.h"
+#include "utils.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//EXPORTS
+//Default constructor
+exported_function::exported_function()
+ :ordinal_(0), rva_(0), has_name_(false), name_ordinal_(0), forward_(false)
+{}
+
+//Returns ordinal of function (actually, ordinal = hint + ordinal base)
+uint16_t exported_function::get_ordinal() const
+{
+ return ordinal_;
+}
+
+//Returns RVA of function
+uint32_t exported_function::get_rva() const
+{
+ return rva_;
+}
+
+//Returns name of function
+const std::string& exported_function::get_name() const
+{
+ return name_;
+}
+
+//Returns true if function has name and name ordinal
+bool exported_function::has_name() const
+{
+ return has_name_;
+}
+
+//Returns name ordinal of function
+uint16_t exported_function::get_name_ordinal() const
+{
+ return name_ordinal_;
+}
+
+//Returns true if function is forwarded to other library
+bool exported_function::is_forwarded() const
+{
+ return forward_;
+}
+
+//Returns the name of forwarded function
+const std::string& exported_function::get_forwarded_name() const
+{
+ return forward_name_;
+}
+
+//Sets ordinal of function
+void exported_function::set_ordinal(uint16_t ordinal)
+{
+ ordinal_ = ordinal;
+}
+
+//Sets RVA of function
+void exported_function::set_rva(uint32_t rva)
+{
+ rva_ = rva;
+}
+
+//Sets name of function (or clears it, if empty name is passed)
+void exported_function::set_name(const std::string& name)
+{
+ name_ = name;
+ has_name_ = !name.empty();
+}
+
+//Sets name ordinal
+void exported_function::set_name_ordinal(uint16_t name_ordinal)
+{
+ name_ordinal_ = name_ordinal;
+}
+
+//Sets forwarded function name (or clears it, if empty name is passed)
+void exported_function::set_forwarded_name(const std::string& name)
+{
+ forward_name_ = name;
+ forward_ = !name.empty();
+}
+
+//Default constructor
+export_info::export_info()
+ :characteristics_(0),
+ timestamp_(0),
+ major_version_(0),
+ minor_version_(0),
+ ordinal_base_(0),
+ number_of_functions_(0),
+ number_of_names_(0),
+ address_of_functions_(0),
+ address_of_names_(0),
+ address_of_name_ordinals_(0)
+{}
+
+//Returns characteristics
+uint32_t export_info::get_characteristics() const
+{
+ return characteristics_;
+}
+
+//Returns timestamp
+uint32_t export_info::get_timestamp() const
+{
+ return timestamp_;
+}
+
+//Returns major version
+uint16_t export_info::get_major_version() const
+{
+ return major_version_;
+}
+
+//Returns minor version
+uint16_t export_info::get_minor_version() const
+{
+ return minor_version_;
+}
+
+//Returns DLL name
+const std::string& export_info::get_name() const
+{
+ return name_;
+}
+
+//Returns ordinal base
+uint32_t export_info::get_ordinal_base() const
+{
+ return ordinal_base_;
+}
+
+//Returns number of functions
+uint32_t export_info::get_number_of_functions() const
+{
+ return number_of_functions_;
+}
+
+//Returns number of function names
+uint32_t export_info::get_number_of_names() const
+{
+ return number_of_names_;
+}
+
+//Returns RVA of function address table
+uint32_t export_info::get_rva_of_functions() const
+{
+ return address_of_functions_;
+}
+
+//Returns RVA of function name address table
+uint32_t export_info::get_rva_of_names() const
+{
+ return address_of_names_;
+}
+
+//Returns RVA of name ordinals table
+uint32_t export_info::get_rva_of_name_ordinals() const
+{
+ return address_of_name_ordinals_;
+}
+
+//Sets characteristics
+void export_info::set_characteristics(uint32_t characteristics)
+{
+ characteristics_ = characteristics;
+}
+
+//Sets timestamp
+void export_info::set_timestamp(uint32_t timestamp)
+{
+ timestamp_ = timestamp;
+}
+
+//Sets major version
+void export_info::set_major_version(uint16_t major_version)
+{
+ major_version_ = major_version;
+}
+
+//Sets minor version
+void export_info::set_minor_version(uint16_t minor_version)
+{
+ minor_version_ = minor_version;
+}
+
+//Sets DLL name
+void export_info::set_name(const std::string& name)
+{
+ name_ = name;
+}
+
+//Sets ordinal base
+void export_info::set_ordinal_base(uint32_t ordinal_base)
+{
+ ordinal_base_ = ordinal_base;
+}
+
+//Sets number of functions
+void export_info::set_number_of_functions(uint32_t number_of_functions)
+{
+ number_of_functions_ = number_of_functions;
+}
+
+//Sets number of function names
+void export_info::set_number_of_names(uint32_t number_of_names)
+{
+ number_of_names_ = number_of_names;
+}
+
+//Sets RVA of function address table
+void export_info::set_rva_of_functions(uint32_t rva_of_functions)
+{
+ address_of_functions_ = rva_of_functions;
+}
+
+//Sets RVA of function name address table
+void export_info::set_rva_of_names(uint32_t rva_of_names)
+{
+ address_of_names_ = rva_of_names;
+}
+
+//Sets RVA of name ordinals table
+void export_info::set_rva_of_name_ordinals(uint32_t rva_of_name_ordinals)
+{
+ address_of_name_ordinals_ = rva_of_name_ordinals;
+}
+
+const exported_functions_list get_exported_functions(const pe_base& pe, export_info* info);
+
+//Returns array of exported functions
+const exported_functions_list get_exported_functions(const pe_base& pe)
+{
+ return get_exported_functions(pe, 0);
+}
+
+//Returns array of exported functions and information about export
+const exported_functions_list get_exported_functions(const pe_base& pe, export_info& info)
+{
+ return get_exported_functions(pe, &info);
+}
+
+//Helper: sorts exported function list by ordinals
+struct ordinal_sorter
+{
+public:
+ bool operator()(const exported_function& func1, const exported_function& func2) const;
+};
+
+//Returns array of exported functions and information about export (if info != 0)
+const exported_functions_list get_exported_functions(const pe_base& pe, export_info* info)
+{
+ //Returned exported functions info array
+ std::vector<exported_function> ret;
+
+ if(pe.has_exports())
+ {
+ //Check the length in bytes of the section containing export directory
+ if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_export),
+ pe.get_directory_rva(image_directory_entry_export), section_data_virtual, true)
+ < sizeof(image_export_directory))
+ throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory);
+
+ image_export_directory exports = pe.section_data_from_rva<image_export_directory>(pe.get_directory_rva(image_directory_entry_export), section_data_virtual, true);
+
+ unsigned long max_name_length;
+
+ if(info)
+ {
+ //Save some export info data
+ info->set_characteristics(exports.Characteristics);
+ info->set_major_version(exports.MajorVersion);
+ info->set_minor_version(exports.MinorVersion);
+
+ //Get byte count that we have for dll name
+ if((max_name_length = pe.section_data_length_from_rva(exports.Name, exports.Name, section_data_virtual, true)) < 2)
+ throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory);
+
+ //Get dll name pointer
+ const char* dll_name = pe.section_data_from_rva(exports.Name, section_data_virtual, true);
+
+ //Check for null-termination
+ if(!pe_utils::is_null_terminated(dll_name, max_name_length))
+ throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory);
+
+ //Save the rest of export information data
+ info->set_name(dll_name);
+ info->set_number_of_functions(exports.NumberOfFunctions);
+ info->set_number_of_names(exports.NumberOfNames);
+ info->set_ordinal_base(exports.Base);
+ info->set_rva_of_functions(exports.AddressOfFunctions);
+ info->set_rva_of_names(exports.AddressOfNames);
+ info->set_rva_of_name_ordinals(exports.AddressOfNameOrdinals);
+ info->set_timestamp(exports.TimeDateStamp);
+ }
+
+ if(!exports.NumberOfFunctions)
+ return ret;
+
+ //Check IMAGE_EXPORT_DIRECTORY fields
+ if(exports.NumberOfNames > exports.NumberOfFunctions)
+ throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory);
+
+ //Check some export directory fields
+ if((!exports.AddressOfNameOrdinals && exports.AddressOfNames) ||
+ (exports.AddressOfNameOrdinals && !exports.AddressOfNames) ||
+ !exports.AddressOfFunctions
+ || exports.NumberOfFunctions >= pe_utils::max_dword / sizeof(uint32_t)
+ || exports.NumberOfNames > pe_utils::max_dword / sizeof(uint32_t)
+ || !pe_utils::is_sum_safe(exports.AddressOfFunctions, exports.NumberOfFunctions * sizeof(uint32_t))
+ || !pe_utils::is_sum_safe(exports.AddressOfNames, exports.NumberOfNames * sizeof(uint32_t))
+ || !pe_utils::is_sum_safe(exports.AddressOfNameOrdinals, exports.NumberOfFunctions * sizeof(uint32_t))
+ || !pe_utils::is_sum_safe(pe.get_directory_rva(image_directory_entry_export), pe.get_directory_size(image_directory_entry_export)))
+ throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory);
+
+ //Check if it is enough bytes to hold AddressOfFunctions table
+ if(pe.section_data_length_from_rva(exports.AddressOfFunctions, exports.AddressOfFunctions, section_data_virtual, true)
+ < exports.NumberOfFunctions * sizeof(uint32_t))
+ throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory);
+
+ if(exports.AddressOfNames)
+ {
+ //Check if it is enough bytes to hold name and ordinal tables
+ if(pe.section_data_length_from_rva(exports.AddressOfNameOrdinals, exports.AddressOfNameOrdinals, section_data_virtual, true)
+ < exports.NumberOfNames * sizeof(uint16_t))
+ throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory);
+
+ if(pe.section_data_length_from_rva(exports.AddressOfNames, exports.AddressOfNames, section_data_virtual, true)
+ < exports.NumberOfNames * sizeof(uint32_t))
+ throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory);
+ }
+
+ for(uint32_t ordinal = 0; ordinal < exports.NumberOfFunctions; ordinal++)
+ {
+ //Get function address
+ //Sum and multiplication are safe (checked above)
+ uint32_t rva = pe.section_data_from_rva<uint32_t>(exports.AddressOfFunctions + ordinal * sizeof(uint32_t), section_data_virtual, true);
+
+ //If we have a skip
+ if(!rva)
+ continue;
+
+ exported_function func;
+ func.set_rva(rva);
+
+ if(!pe_utils::is_sum_safe(exports.Base, ordinal) || exports.Base + ordinal > pe_utils::max_word)
+ throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory);
+
+ func.set_ordinal(static_cast<uint16_t>(ordinal + exports.Base));
+
+ //Scan for function name ordinal
+ for(uint32_t i = 0; i < exports.NumberOfNames; i++)
+ {
+ uint16_t ordinal2 = pe.section_data_from_rva<uint16_t>(exports.AddressOfNameOrdinals + i * sizeof(uint16_t), section_data_virtual, true);
+
+ //If function has name (and name ordinal)
+ if(ordinal == ordinal2)
+ {
+ //Get function name
+ //Sum and multiplication are safe (checked above)
+ uint32_t function_name_rva = pe.section_data_from_rva<uint32_t>(exports.AddressOfNames + i * sizeof(uint32_t), section_data_virtual, true);
+
+ //Get byte count that we have for function name
+ if((max_name_length = pe.section_data_length_from_rva(function_name_rva, function_name_rva, section_data_virtual, true)) < 2)
+ throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory);
+
+ //Get function name pointer
+ const char* func_name = pe.section_data_from_rva(function_name_rva, section_data_virtual, true);
+
+ //Check for null-termination
+ if(!pe_utils::is_null_terminated(func_name, max_name_length))
+ throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory);
+
+ //Save function info
+ func.set_name(func_name);
+ func.set_name_ordinal(ordinal2);
+
+ //If the function is just a redirect, save its name
+ if(rva >= pe.get_directory_rva(image_directory_entry_export) + sizeof(image_directory_entry_export) &&
+ rva < pe.get_directory_rva(image_directory_entry_export) + pe.get_directory_size(image_directory_entry_export))
+ {
+ if((max_name_length = pe.section_data_length_from_rva(rva, rva, section_data_virtual, true)) < 2)
+ throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory);
+
+ //Get forwarded function name pointer
+ const char* forwarded_func_name = pe.section_data_from_rva(rva, section_data_virtual, true);
+
+ //Check for null-termination
+ if(!pe_utils::is_null_terminated(forwarded_func_name, max_name_length))
+ throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory);
+
+ //Set the name of forwarded function
+ func.set_forwarded_name(forwarded_func_name);
+ }
+
+ break;
+ }
+ }
+
+ //Add function info to output array
+ ret.push_back(func);
+ }
+ }
+
+ return ret;
+}
+
+//Helper export functions
+//Returns pair: <ordinal base for supplied functions; maximum ordinal value for supplied functions>
+const std::pair<uint16_t, uint16_t> get_export_ordinal_limits(const exported_functions_list& exports)
+{
+ if(exports.empty())
+ return std::make_pair(0, 0);
+
+ uint16_t max_ordinal = 0; //Maximum ordinal number
+ uint16_t ordinal_base = pe_utils::max_word; //Minimum ordinal value
+ for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it)
+ {
+ const exported_function& func = (*it);
+
+ //Calculate maximum and minimum ordinal numbers
+ max_ordinal = std::max<uint16_t>(max_ordinal, func.get_ordinal());
+ ordinal_base = std::min<uint16_t>(ordinal_base, func.get_ordinal());
+ }
+
+ return std::make_pair(ordinal_base, max_ordinal);
+}
+
+//Checks if exported function name already exists
+bool exported_name_exists(const std::string& function_name, const exported_functions_list& exports)
+{
+ for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it)
+ {
+ if((*it).has_name() && (*it).get_name() == function_name)
+ return true;
+ }
+
+ return false;
+}
+
+//Checks if exported function name already exists
+bool exported_ordinal_exists(uint16_t ordinal, const exported_functions_list& exports)
+{
+ for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it)
+ {
+ if((*it).get_ordinal() == ordinal)
+ return true;
+ }
+
+ return false;
+}
+
+//Helper: sorts exported function list by ordinals
+bool ordinal_sorter::operator()(const exported_function& func1, const exported_function& func2) const
+{
+ return func1.get_ordinal() < func2.get_ordinal();
+}
+
+//Export directory rebuilder
+//info - export information
+//exported_functions_list - list of exported functions
+//exports_section - section where export directory will be placed (must be attached to PE image)
+//offset_from_section_start - offset from exports_section raw data start
+//save_to_pe_headers - if true, new export directory information will be saved to PE image headers
+//auto_strip_last_section - if true and exports are placed in the last section, it will be automatically stripped
+//number_of_functions and number_of_names parameters don't matter in "info" when rebuilding, they're calculated independently
+//characteristics, major_version, minor_version, timestamp and name are the only used members of "info" structure
+//Returns new export directory information
+//exported_functions_list is copied intentionally to be sorted by ordinal values later
+//Name ordinals in exported function don't matter, they will be recalculated
+const image_directory rebuild_exports(pe_base& pe, const export_info& info, exported_functions_list exports, section& exports_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section)
+{
+ //Check that exports_section is attached to this PE image
+ if(!pe.section_attached(exports_section))
+ throw pe_exception("Exports section must be attached to PE file", pe_exception::section_is_not_attached);
+
+ //Needed space for strings
+ uint32_t needed_size_for_strings = static_cast<uint32_t>(info.get_name().length() + 1);
+ uint32_t number_of_names = 0; //Number of named functions
+ uint32_t max_ordinal = 0; //Maximum ordinal number
+ uint32_t ordinal_base = static_cast<uint32_t>(-1); //Minimum ordinal value
+
+ if(exports.empty())
+ ordinal_base = info.get_ordinal_base();
+
+ uint32_t needed_size_for_function_names = 0; //Needed space for function name strings
+ uint32_t needed_size_for_function_forwards = 0; //Needed space for function forwards names
+
+ //List all exported functions
+ //Calculate needed size for function list
+ {
+ //Also check that there're no duplicate names and ordinals
+ std::set<std::string> used_function_names;
+ std::set<uint16_t> used_function_ordinals;
+
+ for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it)
+ {
+ const exported_function& func = (*it);
+ //Calculate maximum and minimum ordinal numbers
+ max_ordinal = std::max<uint32_t>(max_ordinal, func.get_ordinal());
+ ordinal_base = std::min<uint32_t>(ordinal_base, func.get_ordinal());
+
+ //Check if ordinal is unique
+ if(!used_function_ordinals.insert(func.get_ordinal()).second)
+ throw pe_exception("Duplicate exported function ordinal", pe_exception::duplicate_exported_function_ordinal);
+
+ if(func.has_name())
+ {
+ //If function is named
+ ++number_of_names;
+ needed_size_for_function_names += static_cast<uint32_t>(func.get_name().length() + 1);
+
+ //Check if it's name and name ordinal are unique
+ if(!used_function_names.insert(func.get_name()).second)
+ throw pe_exception("Duplicate exported function name", pe_exception::duplicate_exported_function_name);
+ }
+
+ //If function is forwarded to another DLL
+ if(func.is_forwarded())
+ needed_size_for_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1);
+ }
+ }
+
+ //Sort functions by ordinal value
+ std::sort(exports.begin(), exports.end(), ordinal_sorter());
+
+ //Calculate needed space for different things...
+ needed_size_for_strings += needed_size_for_function_names;
+ needed_size_for_strings += needed_size_for_function_forwards;
+ uint32_t needed_size_for_function_name_ordinals = number_of_names * sizeof(uint16_t);
+ uint32_t needed_size_for_function_name_rvas = number_of_names * sizeof(uint32_t);
+ uint32_t needed_size_for_function_addresses = (max_ordinal - ordinal_base + 1) * sizeof(uint32_t);
+
+ //Export directory header will be placed first
+ uint32_t directory_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t));
+
+ uint32_t needed_size = sizeof(image_export_directory); //Calculate needed size for export tables and strings
+ //sizeof(IMAGE_EXPORT_DIRECTORY) = export directory header
+
+ //Total needed space...
+ needed_size += needed_size_for_function_name_ordinals; //For list of names ordinals
+ needed_size += needed_size_for_function_addresses; //For function RVAs
+ needed_size += needed_size_for_strings; //For all strings
+ needed_size += needed_size_for_function_name_rvas; //For function name strings RVAs
+
+ //Check if exports_section is last one. If it's not, check if there's enough place for exports data
+ if(&exports_section != &*(pe.get_image_sections().end() - 1) &&
+ (exports_section.empty() || pe_utils::align_up(exports_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + directory_pos))
+ throw pe_exception("Insufficient space for export directory", pe_exception::insufficient_space);
+
+ std::string& raw_data = exports_section.get_raw_data();
+
+ //This will be done only if exports_section is the last section of image or for section with unaligned raw length of data
+ if(raw_data.length() < needed_size + directory_pos)
+ raw_data.resize(needed_size + directory_pos); //Expand section raw data
+
+ //Library name will be placed after it
+ uint32_t current_pos_of_function_names = static_cast<uint32_t>(info.get_name().length() + 1 + directory_pos + sizeof(image_export_directory));
+ //Next - function names
+ uint32_t current_pos_of_function_name_ordinals = current_pos_of_function_names + needed_size_for_function_names;
+ //Next - function name ordinals
+ uint32_t current_pos_of_function_forwards = current_pos_of_function_name_ordinals + needed_size_for_function_name_ordinals;
+ //Finally - function addresses
+ uint32_t current_pos_of_function_addresses = current_pos_of_function_forwards + needed_size_for_function_forwards;
+ //Next - function names RVAs
+ uint32_t current_pos_of_function_names_rvas = current_pos_of_function_addresses + needed_size_for_function_addresses;
+
+ {
+ //Create export directory and fill it
+ image_export_directory dir = {0};
+ dir.Characteristics = info.get_characteristics();
+ dir.MajorVersion = info.get_major_version();
+ dir.MinorVersion = info.get_minor_version();
+ dir.TimeDateStamp = info.get_timestamp();
+ dir.NumberOfFunctions = max_ordinal - ordinal_base + 1;
+ dir.NumberOfNames = number_of_names;
+ dir.Base = ordinal_base;
+ dir.AddressOfFunctions = pe.rva_from_section_offset(exports_section, current_pos_of_function_addresses);
+ dir.AddressOfNameOrdinals = pe.rva_from_section_offset(exports_section, current_pos_of_function_name_ordinals);
+ dir.AddressOfNames = pe.rva_from_section_offset(exports_section, current_pos_of_function_names_rvas);
+ dir.Name = pe.rva_from_section_offset(exports_section, directory_pos + sizeof(image_export_directory));
+
+ //Save it
+ memcpy(&raw_data[directory_pos], &dir, sizeof(dir));
+ }
+
+ //Sve library name
+ memcpy(&raw_data[directory_pos + sizeof(image_export_directory)], info.get_name().c_str(), info.get_name().length() + 1);
+
+ //A map to sort function names alphabetically
+ typedef std::map<std::string, uint16_t> funclist; //function name; function name ordinal
+ funclist funcs;
+
+ uint32_t last_ordinal = ordinal_base;
+ //Enumerate all exported functions
+ for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it)
+ {
+ const exported_function& func = (*it);
+
+ //If we're skipping some ordinals...
+ if(func.get_ordinal() > last_ordinal)
+ {
+ //Fill this function RVAs data with zeros
+ uint32_t len = sizeof(uint32_t) * (func.get_ordinal() - last_ordinal - 1);
+ if(len)
+ {
+ memset(&raw_data[current_pos_of_function_addresses], 0, len);
+ current_pos_of_function_addresses += len;
+ }
+
+ //Save last encountered ordinal
+ last_ordinal = func.get_ordinal();
+ }
+
+ //If function is named, save its name ordinal and name in sorted alphabetically order
+ if(func.has_name())
+ funcs.insert(std::make_pair(func.get_name(), static_cast<uint16_t>(func.get_ordinal() - ordinal_base))); //Calculate name ordinal
+
+ //If function is forwarded to another DLL
+ if(func.is_forwarded())
+ {
+ //Write its forwarded name and its RVA
+ uint32_t function_rva = pe.rva_from_section_offset(exports_section, current_pos_of_function_forwards);
+ memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva));
+ current_pos_of_function_addresses += sizeof(function_rva);
+
+ memcpy(&raw_data[current_pos_of_function_forwards], func.get_forwarded_name().c_str(), func.get_forwarded_name().length() + 1);
+ current_pos_of_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1);
+ }
+ else
+ {
+ //Write actual function RVA
+ uint32_t function_rva = func.get_rva();
+ memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva));
+ current_pos_of_function_addresses += sizeof(function_rva);
+ }
+ }
+
+ //Enumerate sorted function names
+ for(funclist::const_iterator it = funcs.begin(); it != funcs.end(); ++it)
+ {
+ //Save function name RVA
+ uint32_t function_name_rva = pe.rva_from_section_offset(exports_section, current_pos_of_function_names);
+ memcpy(&raw_data[current_pos_of_function_names_rvas], &function_name_rva, sizeof(function_name_rva));
+ current_pos_of_function_names_rvas += sizeof(function_name_rva);
+
+ //Save function name
+ memcpy(&raw_data[current_pos_of_function_names], (*it).first.c_str(), (*it).first.length() + 1);
+ current_pos_of_function_names += static_cast<uint32_t>((*it).first.length() + 1);
+
+ //Save function name ordinal
+ uint16_t name_ordinal = (*it).second;
+ memcpy(&raw_data[current_pos_of_function_name_ordinals], &name_ordinal, sizeof(name_ordinal));
+ current_pos_of_function_name_ordinals += sizeof(name_ordinal);
+ }
+
+ //Adjust section raw and virtual sizes
+ pe.recalculate_section_sizes(exports_section, auto_strip_last_section);
+
+ image_directory ret(pe.rva_from_section_offset(exports_section, directory_pos), needed_size);
+
+ //If auto-rewrite of PE headers is required
+ if(save_to_pe_header)
+ {
+ pe.set_directory_rva(image_directory_entry_export, ret.get_rva());
+ pe.set_directory_size(image_directory_entry_export, ret.get_size());
+ }
+
+ return ret;
+}
+}
diff --git a/tools/pe_bliss/pe_exports.h b/tools/pe_bliss/pe_exports.h
new file mode 100644
index 0000000000..127cf86ed6
--- /dev/null
+++ b/tools/pe_bliss/pe_exports.h
@@ -0,0 +1,184 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <vector>
+#include <string>
+#include "pe_structures.h"
+#include "pe_base.h"
+#include "pe_directory.h"
+
+namespace pe_bliss
+{
+//Class representing exported function
+class exported_function
+{
+public:
+ //Default constructor
+ exported_function();
+
+ //Returns ordinal of function (actually, ordinal = hint + ordinal base)
+ uint16_t get_ordinal() const;
+
+ //Returns RVA of function
+ uint32_t get_rva() const;
+
+ //Returns true if function has name and name ordinal
+ bool has_name() const;
+ //Returns name of function
+ const std::string& get_name() const;
+ //Returns name ordinal of function
+ uint16_t get_name_ordinal() const;
+
+ //Returns true if function is forwarded to other library
+ bool is_forwarded() const;
+ //Returns the name of forwarded function
+ const std::string& get_forwarded_name() const;
+
+public: //Setters do not change everything inside image, they are used by PE class
+ //You can also use them to rebuild export directory
+
+ //Sets ordinal of function
+ void set_ordinal(uint16_t ordinal);
+
+ //Sets RVA of function
+ void set_rva(uint32_t rva);
+
+ //Sets name of function (or clears it, if empty name is passed)
+ void set_name(const std::string& name);
+ //Sets name ordinal
+ void set_name_ordinal(uint16_t name_ordinal);
+
+ //Sets forwarded function name (or clears it, if empty name is passed)
+ void set_forwarded_name(const std::string& name);
+
+private:
+ uint16_t ordinal_; //Function ordinal
+ uint32_t rva_; //Function RVA
+ std::string name_; //Function name
+ bool has_name_; //true == function has name
+ uint16_t name_ordinal_; //Function name ordinal
+ bool forward_; //true == function is forwarded
+ std::string forward_name_; //Name of forwarded function
+};
+
+//Class representing export information
+class export_info
+{
+public:
+ //Default constructor
+ export_info();
+
+ //Returns characteristics
+ uint32_t get_characteristics() const;
+ //Returns timestamp
+ uint32_t get_timestamp() const;
+ //Returns major version
+ uint16_t get_major_version() const;
+ //Returns minor version
+ uint16_t get_minor_version() const;
+ //Returns DLL name
+ const std::string& get_name() const;
+ //Returns ordinal base
+ uint32_t get_ordinal_base() const;
+ //Returns number of functions
+ uint32_t get_number_of_functions() const;
+ //Returns number of function names
+ uint32_t get_number_of_names() const;
+ //Returns RVA of function address table
+ uint32_t get_rva_of_functions() const;
+ //Returns RVA of function name address table
+ uint32_t get_rva_of_names() const;
+ //Returns RVA of name ordinals table
+ uint32_t get_rva_of_name_ordinals() const;
+
+public: //Setters do not change everything inside image, they are used by PE class
+ //You can also use them to rebuild export directory using rebuild_exports
+
+ //Sets characteristics
+ void set_characteristics(uint32_t characteristics);
+ //Sets timestamp
+ void set_timestamp(uint32_t timestamp);
+ //Sets major version
+ void set_major_version(uint16_t major_version);
+ //Sets minor version
+ void set_minor_version(uint16_t minor_version);
+ //Sets DLL name
+ void set_name(const std::string& name);
+ //Sets ordinal base
+ void set_ordinal_base(uint32_t ordinal_base);
+ //Sets number of functions
+ void set_number_of_functions(uint32_t number_of_functions);
+ //Sets number of function names
+ void set_number_of_names(uint32_t number_of_names);
+ //Sets RVA of function address table
+ void set_rva_of_functions(uint32_t rva_of_functions);
+ //Sets RVA of function name address table
+ void set_rva_of_names(uint32_t rva_of_names);
+ //Sets RVA of name ordinals table
+ void set_rva_of_name_ordinals(uint32_t rva_of_name_ordinals);
+
+private:
+ uint32_t characteristics_;
+ uint32_t timestamp_;
+ uint16_t major_version_;
+ uint16_t minor_version_;
+ std::string name_;
+ uint32_t ordinal_base_;
+ uint32_t number_of_functions_;
+ uint32_t number_of_names_;
+ uint32_t address_of_functions_;
+ uint32_t address_of_names_;
+ uint32_t address_of_name_ordinals_;
+};
+
+//Exported functions list typedef
+typedef std::vector<exported_function> exported_functions_list;
+
+//Returns array of exported functions
+const exported_functions_list get_exported_functions(const pe_base& pe);
+//Returns array of exported functions and information about export
+const exported_functions_list get_exported_functions(const pe_base& pe, export_info& info);
+
+//Helper export functions
+//Returns pair: <ordinal base for supplied functions; maximum ordinal value for supplied functions>
+const std::pair<uint16_t, uint16_t> get_export_ordinal_limits(const exported_functions_list& exports);
+
+//Checks if exported function name already exists
+bool exported_name_exists(const std::string& function_name, const exported_functions_list& exports);
+
+//Checks if exported function ordinal already exists
+bool exported_ordinal_exists(uint16_t ordinal, const exported_functions_list& exports);
+
+//Export directory rebuilder
+//info - export information
+//exported_functions_list - list of exported functions
+//exports_section - section where export directory will be placed (must be attached to PE image)
+//offset_from_section_start - offset from exports_section raw data start
+//save_to_pe_headers - if true, new export directory information will be saved to PE image headers
+//auto_strip_last_section - if true and exports are placed in the last section, it will be automatically stripped
+//number_of_functions and number_of_names parameters don't matter in "info" when rebuilding, they're calculated independently
+//characteristics, major_version, minor_version, timestamp and name are the only used members of "info" structure
+//Returns new export directory information
+//exported_functions_list is copied intentionally to be sorted by ordinal values later
+//Name ordinals in exported function don't matter, they will be recalculated
+const image_directory rebuild_exports(pe_base& pe, const export_info& info, exported_functions_list exports, section& exports_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true);
+}
diff --git a/tools/pe_bliss/pe_factory.cpp b/tools/pe_bliss/pe_factory.cpp
new file mode 100644
index 0000000000..f6d8a3e1ed
--- /dev/null
+++ b/tools/pe_bliss/pe_factory.cpp
@@ -0,0 +1,43 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "pe_factory.h"
+#include "pe_properties_generic.h"
+
+namespace pe_bliss
+{
+pe_base pe_factory::create_pe(std::istream& file, bool read_debug_raw_data)
+{
+ return pe_base::get_pe_type(file) == pe_type_32
+ ? pe_base(file, pe_properties_32(), read_debug_raw_data)
+ : pe_base(file, pe_properties_64(), read_debug_raw_data);
+}
+
+pe_base pe_factory::create_pe(const char* file_path, bool read_debug_raw_data)
+{
+ std::ifstream pe_file(file_path, std::ios::in | std::ios::binary);
+ if(!pe_file)
+ {
+ throw pe_exception("Error in open file.", pe_exception::stream_is_bad);
+ }
+ return pe_factory::create_pe(pe_file,read_debug_raw_data);
+}
+}
diff --git a/tools/pe_bliss/pe_factory.h b/tools/pe_bliss/pe_factory.h
new file mode 100644
index 0000000000..60b42d9b71
--- /dev/null
+++ b/tools/pe_bliss/pe_factory.h
@@ -0,0 +1,39 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <memory>
+#include <istream>
+#include <fstream>
+#include "pe_base.h"
+
+namespace pe_bliss
+{
+class pe_factory
+{
+public:
+ //Creates pe_base class instance from PE or PE+ istream
+ //If read_bound_import_raw_data, raw bound import data will be read (used to get bound import info)
+ //If read_debug_raw_data, raw debug data will be read (used to get image debug info)
+ static pe_base create_pe(std::istream& file, bool read_debug_raw_data = true);
+ static pe_base create_pe(const char* file_path, bool read_debug_raw_data = true);
+};
+}
diff --git a/tools/pe_bliss/pe_imports.cpp b/tools/pe_bliss/pe_imports.cpp
new file mode 100644
index 0000000000..0a6c01d6c0
--- /dev/null
+++ b/tools/pe_bliss/pe_imports.cpp
@@ -0,0 +1,777 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <string.h>
+#include "pe_imports.h"
+#include "pe_properties_generic.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//IMPORTS
+//Default constructor
+//If set_to_pe_headers = true, IMAGE_DIRECTORY_ENTRY_IMPORT entry will be reset
+//to new value after import rebuilding
+//If auto_zero_directory_entry_iat = true, IMAGE_DIRECTORY_ENTRY_IAT will be set to zero
+//IMAGE_DIRECTORY_ENTRY_IAT is used by loader to temporarily make section, where IMAGE_DIRECTORY_ENTRY_IAT RVA points, writeable
+//to be able to modify IAT thunks
+import_rebuilder_settings::import_rebuilder_settings(bool set_to_pe_headers, bool auto_zero_directory_entry_iat)
+ :offset_from_section_start_(0),
+ build_original_iat_(true),
+ save_iat_and_original_iat_rvas_(true),
+ fill_missing_original_iats_(false),
+ set_to_pe_headers_(set_to_pe_headers),
+ zero_directory_entry_iat_(auto_zero_directory_entry_iat),
+ rewrite_iat_and_original_iat_contents_(false),
+ auto_strip_last_section_(true)
+{}
+
+//Returns offset from section start where import directory data will be placed
+uint32_t import_rebuilder_settings::get_offset_from_section_start() const
+{
+ return offset_from_section_start_;
+}
+
+//Returns true if Original import address table (IAT) will be rebuilt
+bool import_rebuilder_settings::build_original_iat() const
+{
+ return build_original_iat_;
+}
+
+//Returns true if Original import address and import address tables will not be rebuilt,
+//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero
+bool import_rebuilder_settings::save_iat_and_original_iat_rvas() const
+{
+ return save_iat_and_original_iat_rvas_;
+}
+
+//Returns true if Original import address and import address tables contents will be rewritten
+//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero
+//and save_iat_and_original_iat_rvas is true
+bool import_rebuilder_settings::rewrite_iat_and_original_iat_contents() const
+{
+ return rewrite_iat_and_original_iat_contents_;
+}
+
+//Returns true if original missing IATs will be rebuilt
+//(only if IATs are saved)
+bool import_rebuilder_settings::fill_missing_original_iats() const
+{
+ return fill_missing_original_iats_;
+}
+
+//Returns true if PE headers should be updated automatically after rebuilding of imports
+bool import_rebuilder_settings::auto_set_to_pe_headers() const
+{
+ return set_to_pe_headers_;
+}
+
+//Returns true if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true
+bool import_rebuilder_settings::zero_directory_entry_iat() const
+{
+ return zero_directory_entry_iat_;
+}
+
+//Returns true if the last section should be stripped automatically, if imports are inside it
+bool import_rebuilder_settings::auto_strip_last_section_enabled() const
+{
+ return auto_strip_last_section_;
+}
+
+//Sets offset from section start where import directory data will be placed
+void import_rebuilder_settings::set_offset_from_section_start(uint32_t offset)
+{
+ offset_from_section_start_ = offset;
+}
+
+//Sets if Original import address table (IAT) will be rebuilt
+void import_rebuilder_settings::build_original_iat(bool enable)
+{
+ build_original_iat_ = enable;
+}
+
+//Sets if Original import address and import address tables will not be rebuilt,
+//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero
+void import_rebuilder_settings::save_iat_and_original_iat_rvas(bool enable, bool enable_rewrite_iat_and_original_iat_contents)
+{
+ save_iat_and_original_iat_rvas_ = enable;
+ if(save_iat_and_original_iat_rvas_)
+ rewrite_iat_and_original_iat_contents_ = enable_rewrite_iat_and_original_iat_contents;
+ else
+ rewrite_iat_and_original_iat_contents_ = false;
+}
+
+//Sets if original missing IATs will be rebuilt
+//(only if IATs are saved)
+void import_rebuilder_settings::fill_missing_original_iats(bool enable)
+{
+ fill_missing_original_iats_ = enable;
+}
+
+//Sets if PE headers should be updated automatically after rebuilding of imports
+void import_rebuilder_settings::auto_set_to_pe_headers(bool enable)
+{
+ set_to_pe_headers_ = enable;
+}
+
+//Sets if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true
+void import_rebuilder_settings::zero_directory_entry_iat(bool enable)
+{
+ zero_directory_entry_iat_ = enable;
+}
+
+//Sets if the last section should be stripped automatically, if imports are inside it, default true
+void import_rebuilder_settings::enable_auto_strip_last_section(bool enable)
+{
+ auto_strip_last_section_ = enable;
+}
+
+//Default constructor
+imported_function::imported_function()
+ :hint_(0), ordinal_(0), iat_va_(0)
+{}
+
+//Returns name of function
+const std::string& imported_function::get_name() const
+{
+ return name_;
+}
+
+//Returns true if imported function has name (and hint)
+bool imported_function::has_name() const
+{
+ return !name_.empty();
+}
+
+//Returns hint
+uint16_t imported_function::get_hint() const
+{
+ return hint_;
+}
+
+//Returns ordinal of function
+uint16_t imported_function::get_ordinal() const
+{
+ return ordinal_;
+}
+
+//Returns IAT entry VA (usable if image has both IAT and original IAT and is bound)
+uint64_t imported_function::get_iat_va() const
+{
+ return iat_va_;
+}
+
+//Sets name of function
+void imported_function::set_name(const std::string& name)
+{
+ name_ = name;
+}
+
+//Sets hint
+void imported_function::set_hint(uint16_t hint)
+{
+ hint_ = hint;
+}
+
+//Sets ordinal
+void imported_function::set_ordinal(uint16_t ordinal)
+{
+ ordinal_ = ordinal;
+}
+
+//Sets IAT entry VA (usable if image has both IAT and original IAT and is bound)
+void imported_function::set_iat_va(uint64_t va)
+{
+ iat_va_ = va;
+}
+
+//Default constructor
+import_library::import_library()
+ :rva_to_iat_(0), rva_to_original_iat_(0), timestamp_(0)
+{}
+
+//Returns name of library
+const std::string& import_library::get_name() const
+{
+ return name_;
+}
+
+//Returns RVA to Import Address Table (IAT)
+uint32_t import_library::get_rva_to_iat() const
+{
+ return rva_to_iat_;
+}
+
+//Returns RVA to Original Import Address Table (Original IAT)
+uint32_t import_library::get_rva_to_original_iat() const
+{
+ return rva_to_original_iat_;
+}
+
+//Returns timestamp
+uint32_t import_library::get_timestamp() const
+{
+ return timestamp_;
+}
+
+//Sets name of library
+void import_library::set_name(const std::string& name)
+{
+ name_ = name;
+}
+
+//Sets RVA to Import Address Table (IAT)
+void import_library::set_rva_to_iat(uint32_t rva_to_iat)
+{
+ rva_to_iat_ = rva_to_iat;
+}
+
+//Sets RVA to Original Import Address Table (Original IAT)
+void import_library::set_rva_to_original_iat(uint32_t rva_to_original_iat)
+{
+ rva_to_original_iat_ = rva_to_original_iat;
+}
+
+//Sets timestamp
+void import_library::set_timestamp(uint32_t timestamp)
+{
+ timestamp_ = timestamp;
+}
+
+//Returns imported functions list
+const import_library::imported_list& import_library::get_imported_functions() const
+{
+ return imports_;
+}
+
+//Adds imported function
+void import_library::add_import(const imported_function& func)
+{
+ imports_.push_back(func);
+}
+
+//Clears imported functions list
+void import_library::clear_imports()
+{
+ imports_.clear();
+}
+
+const imported_functions_list get_imported_functions(const pe_base& pe)
+{
+ return (pe.get_pe_type() == pe_type_32 ?
+ get_imported_functions_base<pe_types_class_32>(pe)
+ : get_imported_functions_base<pe_types_class_64>(pe));
+}
+
+const image_directory rebuild_imports(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings)
+{
+ return (pe.get_pe_type() == pe_type_32 ?
+ rebuild_imports_base<pe_types_class_32>(pe, imports, import_section, import_settings)
+ : rebuild_imports_base<pe_types_class_64>(pe, imports, import_section, import_settings));
+}
+
+//Returns imported functions list with related libraries info
+template<typename PEClassType>
+const imported_functions_list get_imported_functions_base(const pe_base& pe)
+{
+ imported_functions_list ret;
+
+ //If image has no imports, return empty array
+ if(!pe.has_imports())
+ return ret;
+
+ unsigned long current_descriptor_pos = pe.get_directory_rva(image_directory_entry_import);
+ //Get first IMAGE_IMPORT_DESCRIPTOR
+ image_import_descriptor import_descriptor = pe.section_data_from_rva<image_import_descriptor>(current_descriptor_pos, section_data_virtual, true);
+
+ //Iterate them until we reach zero-element
+ //We don't need to check correctness of this, because exception will be thrown
+ //inside of loop if we go outsize of section
+ while(import_descriptor.Name)
+ {
+ //Get imported library information
+ import_library lib;
+
+ unsigned long max_name_length;
+ //Get byte count that we have for library name
+ if((max_name_length = pe.section_data_length_from_rva(import_descriptor.Name, import_descriptor.Name, section_data_virtual, true)) < 2)
+ throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory);
+
+ //Get DLL name pointer
+ const char* dll_name = pe.section_data_from_rva(import_descriptor.Name, section_data_virtual, true);
+
+ //Check for null-termination
+ if(!pe_utils::is_null_terminated(dll_name, max_name_length))
+ throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory);
+
+ //Set library name
+ lib.set_name(dll_name);
+ //Set library timestamp
+ lib.set_timestamp(import_descriptor.TimeDateStamp);
+ //Set library RVA to IAT and original IAT
+ lib.set_rva_to_iat(import_descriptor.FirstThunk);
+ lib.set_rva_to_original_iat(import_descriptor.OriginalFirstThunk);
+
+ //Get RVA to IAT (it must be filled by loader when loading PE)
+ uint32_t current_thunk_rva = import_descriptor.FirstThunk;
+ typename PEClassType::BaseSize import_address_table = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_thunk_rva, section_data_virtual, true);
+
+ //Get RVA to original IAT (lookup table), which must handle imported functions names
+ //Some linkers leave this pointer zero-filled
+ //Such image is valid, but it is not possible to restore imported functions names
+ //afted image was loaded, because IAT becomes the only one table
+ //containing both function names and function RVAs after loading
+ uint32_t current_original_thunk_rva = import_descriptor.OriginalFirstThunk;
+ typename PEClassType::BaseSize import_lookup_table = current_original_thunk_rva == 0 ? import_address_table : pe.section_data_from_rva<typename PEClassType::BaseSize>(current_original_thunk_rva, section_data_virtual, true);
+ if(current_original_thunk_rva == 0)
+ current_original_thunk_rva = current_thunk_rva;
+
+ //List all imported functions for current DLL
+ if(import_lookup_table != 0 && import_address_table != 0)
+ {
+ while(true)
+ {
+ //Imported function description
+ imported_function func;
+
+ //Get VA from IAT
+ typename PEClassType::BaseSize address = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_thunk_rva, section_data_virtual, true);
+ //Move pointer
+ current_thunk_rva += sizeof(typename PEClassType::BaseSize);
+
+ //Jump to next DLL if we finished with this one
+ if(!address)
+ break;
+
+ func.set_iat_va(address);
+
+ //Get VA from original IAT
+ typename PEClassType::BaseSize lookup = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_original_thunk_rva, section_data_virtual, true);
+ //Move pointer
+ current_original_thunk_rva += sizeof(typename PEClassType::BaseSize);
+
+ //Check if function is imported by ordinal
+ if((lookup & PEClassType::ImportSnapFlag) != 0)
+ {
+ //Set function ordinal
+ func.set_ordinal(static_cast<uint16_t>(lookup & 0xffff));
+ }
+ else
+ {
+ //Get byte count that we have for function name
+ if(lookup > static_cast<uint32_t>(-1) - sizeof(uint16_t))
+ throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory);
+
+ //Get maximum available length of function name
+ if((max_name_length = pe.section_data_length_from_rva(static_cast<uint32_t>(lookup + sizeof(uint16_t)), static_cast<uint32_t>(lookup + sizeof(uint16_t)), section_data_virtual, true)) < 2)
+ throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory);
+
+ //Get imported function name
+ const char* func_name = pe.section_data_from_rva(static_cast<uint32_t>(lookup + sizeof(uint16_t)), section_data_virtual, true);
+
+ //Check for null-termination
+ if(!pe_utils::is_null_terminated(func_name, max_name_length))
+ throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory);
+
+ //HINT in import table is ORDINAL in export table
+ uint16_t hint = pe.section_data_from_rva<uint16_t>(static_cast<uint32_t>(lookup), section_data_virtual, true);
+
+ //Save hint and name
+ func.set_name(func_name);
+ func.set_hint(hint);
+ }
+
+ //Add function to list
+ lib.add_import(func);
+ }
+ }
+
+ //Check possible overflow
+ if(!pe_utils::is_sum_safe(current_descriptor_pos, sizeof(image_import_descriptor)))
+ throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory);
+
+ //Go to next library
+ current_descriptor_pos += sizeof(image_import_descriptor);
+ import_descriptor = pe.section_data_from_rva<image_import_descriptor>(current_descriptor_pos, section_data_virtual, true);
+
+ //Save import information
+ ret.push_back(lib);
+ }
+
+ //Return resulting list
+ return ret;
+}
+
+
+//Simple import directory rebuilder
+//You can get all image imports with get_imported_functions() function
+//You can use returned value to, for example, add new imported library with some functions
+//to the end of list of imported libraries
+//To keep PE file working, rebuild its imports with save_iat_and_original_iat_rvas = true (default)
+//Don't add new imported functions to existing imported library entries, because this can cause
+//rewriting of some used memory (or other IAT/orig.IAT fields) by system loader
+//The safest way is just adding import libraries with functions to the end of imported_functions_list array
+template<typename PEClassType>
+const image_directory rebuild_imports_base(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings)
+{
+ //Check that import_section is attached to this PE image
+ if(!pe.section_attached(import_section))
+ throw pe_exception("Import section must be attached to PE file", pe_exception::section_is_not_attached);
+
+ uint32_t needed_size = 0; //Calculate needed size for import structures and strings
+ uint32_t needed_size_for_strings = 0; //Calculate needed size for import strings (library and function names and hints)
+ uint32_t size_of_iat = 0; //Size of IAT structures
+
+ needed_size += static_cast<uint32_t>((1 /* ending null descriptor */ + imports.size()) * sizeof(image_import_descriptor));
+
+ //Enumerate imported functions
+ for(imported_functions_list::const_iterator it = imports.begin(); it != imports.end(); ++it)
+ {
+ needed_size_for_strings += static_cast<uint32_t>((*it).get_name().length() + 1 /* nullbyte */);
+
+ const import_library::imported_list& funcs = (*it).get_imported_functions();
+
+ //IMAGE_THUNK_DATA
+ size_of_iat += static_cast<uint32_t>(sizeof(typename PEClassType::BaseSize) * (1 /*ending null */ + funcs.size()));
+
+ //Enumerate all imported functions in library
+ for(import_library::imported_list::const_iterator f = funcs.begin(); f != funcs.end(); ++f)
+ {
+ if((*f).has_name())
+ needed_size_for_strings += static_cast<uint32_t>((*f).get_name().length() + 1 /* nullbyte */ + sizeof(uint16_t) /* hint */);
+ }
+ }
+
+ if(import_settings.build_original_iat() || import_settings.fill_missing_original_iats())
+ needed_size += size_of_iat * 2; //We'll have two similar-sized IATs if we're building original IAT
+ else
+ needed_size += size_of_iat;
+
+ needed_size += sizeof(typename PEClassType::BaseSize); //Maximum align for IAT and original IAT
+
+ //Total needed size for import structures and strings
+ needed_size += needed_size_for_strings;
+
+ //Check if import_section is last one. If it's not, check if there's enough place for import data
+ if(&import_section != &*(pe.get_image_sections().end() - 1) &&
+ (import_section.empty() || pe_utils::align_up(import_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + import_settings.get_offset_from_section_start()))
+ throw pe_exception("Insufficient space for import directory", pe_exception::insufficient_space);
+
+ std::string& raw_data = import_section.get_raw_data();
+
+ //This will be done only if image_section is the last section of image or for section with unaligned raw length of data
+ if(raw_data.length() < needed_size + import_settings.get_offset_from_section_start())
+ raw_data.resize(needed_size + import_settings.get_offset_from_section_start()); //Expand section raw data
+
+ uint32_t current_string_pointer = import_settings.get_offset_from_section_start();/* we will paste structures after strings */
+
+ //Position for IAT
+ uint32_t current_pos_for_iat = pe_utils::align_up(static_cast<uint32_t>(needed_size_for_strings + import_settings.get_offset_from_section_start() + (1 + imports.size()) * sizeof(image_import_descriptor)), sizeof(typename PEClassType::BaseSize));
+ //Position for original IAT
+ uint32_t current_pos_for_original_iat = current_pos_for_iat + size_of_iat;
+ //Position for import descriptors
+ uint32_t current_pos_for_descriptors = needed_size_for_strings + import_settings.get_offset_from_section_start();
+
+ //Build imports
+ for(imported_functions_list::const_iterator it = imports.begin(); it != imports.end(); ++it)
+ {
+ //Create import descriptor
+ image_import_descriptor descr;
+ memset(&descr, 0, sizeof(descr));
+ descr.TimeDateStamp = (*it).get_timestamp(); //Restore timestamp
+ descr.Name = pe.rva_from_section_offset(import_section, current_string_pointer); //Library name RVA
+
+ //If we should save IAT for current import descriptor
+ bool save_iats_for_this_descriptor = import_settings.save_iat_and_original_iat_rvas() && (*it).get_rva_to_iat() != 0;
+ //If we should write original IAT
+ bool write_original_iat = (!save_iats_for_this_descriptor && import_settings.build_original_iat()) || import_settings.fill_missing_original_iats();
+
+ //If we should rewrite saved original IAT for current import descriptor (without changing its position)
+ bool rewrite_saved_original_iat = save_iats_for_this_descriptor && import_settings.rewrite_iat_and_original_iat_contents() && import_settings.build_original_iat();
+ //If we should rewrite saved IAT for current import descriptor (without changing its position)
+ bool rewrite_saved_iat = save_iats_for_this_descriptor && import_settings.rewrite_iat_and_original_iat_contents() && (*it).get_rva_to_iat() != 0;
+
+ //Helper values if we're rewriting existing IAT or orig.IAT
+ uint32_t original_first_thunk = 0;
+ uint32_t first_thunk = 0;
+
+ if(save_iats_for_this_descriptor)
+ {
+ //If there's no original IAT and we're asked to rebuild missing original IATs
+ if(!(*it).get_rva_to_original_iat() && import_settings.fill_missing_original_iats())
+ descr.OriginalFirstThunk = import_settings.build_original_iat() ? pe.rva_from_section_offset(import_section, current_pos_for_original_iat) : 0;
+ else
+ descr.OriginalFirstThunk = import_settings.build_original_iat() ? (*it).get_rva_to_original_iat() : 0;
+
+ descr.FirstThunk = (*it).get_rva_to_iat();
+
+ original_first_thunk = descr.OriginalFirstThunk;
+ first_thunk = descr.FirstThunk;
+
+ if(rewrite_saved_original_iat)
+ {
+ if((*it).get_rva_to_original_iat())
+ write_original_iat = true;
+ else
+ rewrite_saved_original_iat = false;
+ }
+
+ if(rewrite_saved_iat)
+ save_iats_for_this_descriptor = false;
+ }
+ else
+ {
+ //We are creating new IAT and original IAT (if needed)
+ descr.OriginalFirstThunk = import_settings.build_original_iat() ? pe.rva_from_section_offset(import_section, current_pos_for_original_iat) : 0;
+ descr.FirstThunk = pe.rva_from_section_offset(import_section, current_pos_for_iat);
+ }
+
+ //Save import descriptor
+ memcpy(&raw_data[current_pos_for_descriptors], &descr, sizeof(descr));
+ current_pos_for_descriptors += sizeof(descr);
+
+ //Save library name
+ memcpy(&raw_data[current_string_pointer], (*it).get_name().c_str(), (*it).get_name().length() + 1 /* nullbyte */);
+ current_string_pointer += static_cast<uint32_t>((*it).get_name().length() + 1 /* nullbyte */);
+
+ //List all imported functions
+ const import_library::imported_list& funcs = (*it).get_imported_functions();
+ for(import_library::imported_list::const_iterator f = funcs.begin(); f != funcs.end(); ++f)
+ {
+ if((*f).has_name()) //If function is imported by name
+ {
+ //Get RVA of IMAGE_IMPORT_BY_NAME
+ typename PEClassType::BaseSize rva_of_named_import = pe.rva_from_section_offset(import_section, current_string_pointer);
+
+ if(!save_iats_for_this_descriptor)
+ {
+ if(write_original_iat)
+ {
+ //We're creating original IATs - so we can write to IAT saved VA (because IMAGE_IMPORT_BY_NAME will be read
+ //by PE loader from original IAT)
+ typename PEClassType::BaseSize iat_value = static_cast<typename PEClassType::BaseSize>((*f).get_iat_va());
+
+ if(rewrite_saved_iat)
+ {
+ if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(iat_value))
+ throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space);
+
+ memcpy(pe.section_data_from_rva(first_thunk, true), &iat_value, sizeof(iat_value));
+
+ first_thunk += sizeof(iat_value);
+ }
+ else
+ {
+ memcpy(&raw_data[current_pos_for_iat], &iat_value, sizeof(iat_value));
+ current_pos_for_iat += sizeof(rva_of_named_import);
+ }
+ }
+ else
+ {
+ //Else - write to IAT RVA of IMAGE_IMPORT_BY_NAME
+ if(rewrite_saved_iat)
+ {
+ if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(rva_of_named_import))
+ throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space);
+
+ memcpy(pe.section_data_from_rva(first_thunk, true), &rva_of_named_import, sizeof(rva_of_named_import));
+
+ first_thunk += sizeof(rva_of_named_import);
+ }
+ else
+ {
+ memcpy(&raw_data[current_pos_for_iat], &rva_of_named_import, sizeof(rva_of_named_import));
+ current_pos_for_iat += sizeof(rva_of_named_import);
+ }
+ }
+ }
+
+ if(write_original_iat)
+ {
+ if(rewrite_saved_original_iat)
+ {
+ if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(rva_of_named_import))
+ throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space);
+
+ memcpy(pe.section_data_from_rva(original_first_thunk, true), &rva_of_named_import, sizeof(rva_of_named_import));
+
+ original_first_thunk += sizeof(rva_of_named_import);
+ }
+ else
+ {
+ //We're creating original IATs
+ memcpy(&raw_data[current_pos_for_original_iat], &rva_of_named_import, sizeof(rva_of_named_import));
+ current_pos_for_original_iat += sizeof(rva_of_named_import);
+ }
+ }
+
+ //Write IMAGE_IMPORT_BY_NAME (WORD hint + string function name)
+ uint16_t hint = (*f).get_hint();
+ memcpy(&raw_data[current_string_pointer], &hint, sizeof(hint));
+ memcpy(&raw_data[current_string_pointer + sizeof(uint16_t)], (*f).get_name().c_str(), (*f).get_name().length() + 1 /* nullbyte */);
+ current_string_pointer += static_cast<uint32_t>((*f).get_name().length() + 1 /* nullbyte */ + sizeof(uint16_t) /* hint */);
+ }
+ else //Function is imported by ordinal
+ {
+ uint16_t ordinal = (*f).get_ordinal();
+ typename PEClassType::BaseSize thunk_value = ordinal;
+ thunk_value |= PEClassType::ImportSnapFlag; //Imported by ordinal
+
+ if(!save_iats_for_this_descriptor)
+ {
+ if(write_original_iat)
+ {
+ //We're creating original IATs - so we can wtire to IAT saved VA (because ordinal will be read
+ //by PE loader from original IAT)
+ typename PEClassType::BaseSize iat_value = static_cast<typename PEClassType::BaseSize>((*f).get_iat_va());
+ if(rewrite_saved_iat)
+ {
+ if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(iat_value))
+ throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space);
+
+ memcpy(pe.section_data_from_rva(first_thunk, true), &iat_value, sizeof(iat_value));
+
+ first_thunk += sizeof(iat_value);
+ }
+ else
+ {
+ memcpy(&raw_data[current_pos_for_iat], &iat_value, sizeof(iat_value));
+ current_pos_for_iat += sizeof(thunk_value);
+ }
+ }
+ else
+ {
+ //Else - write ordinal to IAT
+ if(rewrite_saved_iat)
+ {
+ if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(thunk_value))
+ throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space);
+
+ memcpy(pe.section_data_from_rva(first_thunk, true), &thunk_value, sizeof(thunk_value));
+
+ first_thunk += sizeof(thunk_value);
+ }
+ else
+ {
+ memcpy(&raw_data[current_pos_for_iat], &thunk_value, sizeof(thunk_value));
+ }
+ }
+ }
+
+ //We're writing ordinal to original IAT slot
+ if(write_original_iat)
+ {
+ if(rewrite_saved_original_iat)
+ {
+ if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(thunk_value))
+ throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space);
+
+ memcpy(pe.section_data_from_rva(original_first_thunk, true), &thunk_value, sizeof(thunk_value));
+
+ original_first_thunk += sizeof(thunk_value);
+ }
+ else
+ {
+ memcpy(&raw_data[current_pos_for_original_iat], &thunk_value, sizeof(thunk_value));
+ current_pos_for_original_iat += sizeof(thunk_value);
+ }
+ }
+ }
+ }
+
+ if(!save_iats_for_this_descriptor)
+ {
+ //Ending null thunks
+ typename PEClassType::BaseSize thunk_value = 0;
+
+ if(rewrite_saved_iat)
+ {
+ if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(thunk_value))
+ throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space);
+
+ memcpy(pe.section_data_from_rva(first_thunk, true), &thunk_value, sizeof(thunk_value));
+
+ first_thunk += sizeof(thunk_value);
+ }
+ else
+ {
+ memcpy(&raw_data[current_pos_for_iat], &thunk_value, sizeof(thunk_value));
+ current_pos_for_iat += sizeof(thunk_value);
+ }
+ }
+
+ if(write_original_iat)
+ {
+ //Ending null thunks
+ typename PEClassType::BaseSize thunk_value = 0;
+
+ if(rewrite_saved_original_iat)
+ {
+ if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(thunk_value))
+ throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space);
+
+ memcpy(pe.section_data_from_rva(original_first_thunk, true), &thunk_value, sizeof(thunk_value));
+
+ original_first_thunk += sizeof(thunk_value);
+ }
+ else
+ {
+ memcpy(&raw_data[current_pos_for_original_iat], &thunk_value, sizeof(thunk_value));
+ current_pos_for_original_iat += sizeof(thunk_value);
+ }
+ }
+ }
+
+ {
+ //Null ending descriptor
+ image_import_descriptor descr;
+ memset(&descr, 0, sizeof(descr));
+ memcpy(&raw_data[current_pos_for_descriptors], &descr, sizeof(descr));
+ }
+
+ //Strip data a little, if we saved some place
+ //We're allocating more space than needed, if present original IAT and IAT are saved
+ raw_data.resize(current_pos_for_original_iat);
+
+ //Adjust section raw and virtual sizes
+ pe.recalculate_section_sizes(import_section, import_settings.auto_strip_last_section_enabled());
+
+ //Return information about rebuilt import directory
+ image_directory ret(pe.rva_from_section_offset(import_section, import_settings.get_offset_from_section_start() + needed_size_for_strings), needed_size - needed_size_for_strings);
+
+ //If auto-rewrite of PE headers is required
+ if(import_settings.auto_set_to_pe_headers())
+ {
+ pe.set_directory_rva(image_directory_entry_import, ret.get_rva());
+ pe.set_directory_size(image_directory_entry_import, ret.get_size());
+
+ //If we are requested to zero IMAGE_DIRECTORY_ENTRY_IAT also
+ if(import_settings.zero_directory_entry_iat())
+ {
+ pe.set_directory_rva(image_directory_entry_iat, 0);
+ pe.set_directory_size(image_directory_entry_iat, 0);
+ }
+ }
+
+ return ret;
+}
+}
diff --git a/tools/pe_bliss/pe_imports.h b/tools/pe_bliss/pe_imports.h
new file mode 100644
index 0000000000..681b5b59bd
--- /dev/null
+++ b/tools/pe_bliss/pe_imports.h
@@ -0,0 +1,208 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <vector>
+#include <string>
+#include "pe_structures.h"
+#include "pe_directory.h"
+#include "pe_base.h"
+
+namespace pe_bliss
+{
+//Class representing imported function
+class imported_function
+{
+public:
+ //Default constructor
+ imported_function();
+
+ //Returns true if imported function has name (and hint)
+ bool has_name() const;
+ //Returns name of function
+ const std::string& get_name() const;
+ //Returns hint
+ uint16_t get_hint() const;
+ //Returns ordinal of function
+ uint16_t get_ordinal() const;
+
+ //Returns IAT entry VA (usable if image has both IAT and original IAT and is bound)
+ uint64_t get_iat_va() const;
+
+public: //Setters do not change everything inside image, they are used by PE class
+ //You also can use them to rebuild image imports
+ //Sets name of function
+ void set_name(const std::string& name);
+ //Sets hint
+ void set_hint(uint16_t hint);
+ //Sets ordinal
+ void set_ordinal(uint16_t ordinal);
+
+ //Sets IAT entry VA (usable if image has both IAT and original IAT and is bound)
+ void set_iat_va(uint64_t rva);
+
+private:
+ std::string name_; //Function name
+ uint16_t hint_; //Hint
+ uint16_t ordinal_; //Ordinal
+ uint64_t iat_va_;
+};
+
+//Class representing imported library information
+class import_library
+{
+public:
+ typedef std::vector<imported_function> imported_list;
+
+public:
+ //Default constructor
+ import_library();
+
+ //Returns name of library
+ const std::string& get_name() const;
+ //Returns RVA to Import Address Table (IAT)
+ uint32_t get_rva_to_iat() const;
+ //Returns RVA to Original Import Address Table (Original IAT)
+ uint32_t get_rva_to_original_iat() const;
+ //Returns timestamp
+ uint32_t get_timestamp() const;
+
+ //Returns imported functions list
+ const imported_list& get_imported_functions() const;
+
+public: //Setters do not change everything inside image, they are used by PE class
+ //You also can use them to rebuild image imports
+ //Sets name of library
+ void set_name(const std::string& name);
+ //Sets RVA to Import Address Table (IAT)
+ void set_rva_to_iat(uint32_t rva_to_iat);
+ //Sets RVA to Original Import Address Table (Original IAT)
+ void set_rva_to_original_iat(uint32_t rva_to_original_iat);
+ //Sets timestamp
+ void set_timestamp(uint32_t timestamp);
+
+ //Adds imported function
+ void add_import(const imported_function& func);
+ //Clears imported functions list
+ void clear_imports();
+
+private:
+ std::string name_; //Library name
+ uint32_t rva_to_iat_; //RVA to IAT
+ uint32_t rva_to_original_iat_; //RVA to original IAT
+ uint32_t timestamp_; //DLL TimeStamp
+
+ imported_list imports_;
+};
+
+//Simple import directory rebuilder
+//Class representing import rebuilder advanced settings
+class import_rebuilder_settings
+{
+public:
+ //Default constructor
+ //Default constructor
+ //If set_to_pe_headers = true, IMAGE_DIRECTORY_ENTRY_IMPORT entry will be reset
+ //to new value after import rebuilding
+ //If auto_zero_directory_entry_iat = true, IMAGE_DIRECTORY_ENTRY_IAT will be set to zero
+ //IMAGE_DIRECTORY_ENTRY_IAT is used by loader to temporarily make section, where IMAGE_DIRECTORY_ENTRY_IAT RVA points, writeable
+ //to be able to modify IAT thunks
+ explicit import_rebuilder_settings(bool set_to_pe_headers = true, bool auto_zero_directory_entry_iat = false);
+
+ //Returns offset from section start where import directory data will be placed
+ uint32_t get_offset_from_section_start() const;
+ //Returns true if Original import address table (IAT) will be rebuilt
+ bool build_original_iat() const;
+
+ //Returns true if Original import address and import address tables will not be rebuilt,
+ //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero
+ bool save_iat_and_original_iat_rvas() const;
+ //Returns true if Original import address and import address tables contents will be rewritten
+ //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero
+ //and save_iat_and_original_iat_rvas is true
+ bool rewrite_iat_and_original_iat_contents() const;
+
+ //Returns true if original missing IATs will be rebuilt
+ //(only if IATs are saved)
+ bool fill_missing_original_iats() const;
+ //Returns true if PE headers should be updated automatically after rebuilding of imports
+ bool auto_set_to_pe_headers() const;
+ //Returns true if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true
+ bool zero_directory_entry_iat() const;
+
+ //Returns true if the last section should be stripped automatically, if imports are inside it
+ bool auto_strip_last_section_enabled() const;
+
+public: //Setters
+ //Sets offset from section start where import directory data will be placed
+ void set_offset_from_section_start(uint32_t offset);
+ //Sets if Original import address table (IAT) will be rebuilt
+ void build_original_iat(bool enable);
+ //Sets if Original import address and import address tables will not be rebuilt,
+ //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero
+ //enable_rewrite_iat_and_original_iat_contents sets if Original import address and import address tables contents will be rewritten
+ //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero
+ //and save_iat_and_original_iat_rvas is true
+ void save_iat_and_original_iat_rvas(bool enable, bool enable_rewrite_iat_and_original_iat_contents = false);
+ //Sets if original missing IATs will be rebuilt
+ //(only if IATs are saved)
+ void fill_missing_original_iats(bool enable);
+ //Sets if PE headers should be updated automatically after rebuilding of imports
+ void auto_set_to_pe_headers(bool enable);
+ //Sets if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true
+ void zero_directory_entry_iat(bool enable);
+
+ //Sets if the last section should be stripped automatically, if imports are inside it, default true
+ void enable_auto_strip_last_section(bool enable);
+
+private:
+ uint32_t offset_from_section_start_;
+ bool build_original_iat_;
+ bool save_iat_and_original_iat_rvas_;
+ bool fill_missing_original_iats_;
+ bool set_to_pe_headers_;
+ bool zero_directory_entry_iat_;
+ bool rewrite_iat_and_original_iat_contents_;
+ bool auto_strip_last_section_;
+};
+
+typedef std::vector<import_library> imported_functions_list;
+
+
+//Returns imported functions list with related libraries info
+const imported_functions_list get_imported_functions(const pe_base& pe);
+
+template<typename PEClassType>
+const imported_functions_list get_imported_functions_base(const pe_base& pe);
+
+
+//You can get all image imports with get_imported_functions() function
+//You can use returned value to, for example, add new imported library with some functions
+//to the end of list of imported libraries
+//To keep PE file working, rebuild its imports with save_iat_and_original_iat_rvas = true (default)
+//Don't add new imported functions to existing imported library entries, because this can cause
+//rewriting of some used memory (or other IAT/orig.IAT fields) by system loader
+//The safest way is just adding import libraries with functions to the end of imported_functions_list array
+const image_directory rebuild_imports(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings = import_rebuilder_settings());
+
+template<typename PEClassType>
+const image_directory rebuild_imports_base(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings = import_rebuilder_settings());
+}
diff --git a/tools/pe_bliss/pe_load_config.cpp b/tools/pe_bliss/pe_load_config.cpp
new file mode 100644
index 0000000000..c05895fecd
--- /dev/null
+++ b/tools/pe_bliss/pe_load_config.cpp
@@ -0,0 +1,557 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <algorithm>
+#include <string.h>
+#include "pe_load_config.h"
+#include "pe_properties_generic.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//IMAGE CONFIG
+//Default constructor
+image_config_info::image_config_info()
+ :time_stamp_(0),
+ major_version_(0), minor_version_(0),
+ global_flags_clear_(0), global_flags_set_(0),
+ critical_section_default_timeout_(0),
+ decommit_free_block_threshold_(0), decommit_total_free_threshold_(0),
+ lock_prefix_table_va_(0),
+ max_allocation_size_(0),
+ virtual_memory_threshold_(0),
+ process_affinity_mask_(0),
+ process_heap_flags_(0),
+ service_pack_version_(0),
+ edit_list_va_(0),
+ security_cookie_va_(0),
+ se_handler_table_va_(0),
+ se_handler_count_(0)
+{}
+
+//Constructors from PE structures
+template<typename ConfigStructure>
+image_config_info::image_config_info(const ConfigStructure& info)
+ :time_stamp_(info.TimeDateStamp),
+ major_version_(info.MajorVersion), minor_version_(info.MinorVersion),
+ global_flags_clear_(info.GlobalFlagsClear), global_flags_set_(info.GlobalFlagsSet),
+ critical_section_default_timeout_(info.CriticalSectionDefaultTimeout),
+ decommit_free_block_threshold_(info.DeCommitFreeBlockThreshold), decommit_total_free_threshold_(info.DeCommitTotalFreeThreshold),
+ lock_prefix_table_va_(info.LockPrefixTable),
+ max_allocation_size_(info.MaximumAllocationSize),
+ virtual_memory_threshold_(info.VirtualMemoryThreshold),
+ process_affinity_mask_(info.ProcessAffinityMask),
+ process_heap_flags_(info.ProcessHeapFlags),
+ service_pack_version_(info.CSDVersion),
+ edit_list_va_(info.EditList),
+ security_cookie_va_(info.SecurityCookie),
+ se_handler_table_va_(info.SEHandlerTable),
+ se_handler_count_(info.SEHandlerCount)
+{}
+
+//Instantiate template constructor with needed structures
+template image_config_info::image_config_info(const image_load_config_directory32& info);
+template image_config_info::image_config_info(const image_load_config_directory64& info);
+
+//Returns the date and time stamp value
+uint32_t image_config_info::get_time_stamp() const
+{
+ return time_stamp_;
+}
+
+//Returns major version number
+uint16_t image_config_info::get_major_version() const
+{
+ return major_version_;
+}
+
+//Returns minor version number
+uint16_t image_config_info::get_minor_version() const
+{
+ return minor_version_;
+}
+
+//Returns clear global flags
+uint32_t image_config_info::get_global_flags_clear() const
+{
+ return global_flags_clear_;
+}
+
+//Returns set global flags
+uint32_t image_config_info::get_global_flags_set() const
+{
+ return global_flags_set_;
+}
+
+//Returns critical section default timeout
+uint32_t image_config_info::get_critical_section_default_timeout() const
+{
+ return critical_section_default_timeout_;
+}
+
+//Get the size of the minimum block that
+//must be freed before it is freed (de-committed), in bytes
+uint64_t image_config_info::get_decommit_free_block_threshold() const
+{
+ return decommit_free_block_threshold_;
+}
+
+//Returns the size of the minimum total memory
+//that must be freed in the process heap before it is freed (de-committed), in bytes
+uint64_t image_config_info::get_decommit_total_free_threshold() const
+{
+ return decommit_total_free_threshold_;
+}
+
+//Returns VA of a list of addresses where the LOCK prefix is used
+uint64_t image_config_info::get_lock_prefix_table_va() const
+{
+ return lock_prefix_table_va_;
+}
+
+//Returns the maximum allocation size, in bytes
+uint64_t image_config_info::get_max_allocation_size() const
+{
+ return max_allocation_size_;
+}
+
+//Returns the maximum block size that can be allocated from heap segments, in bytes
+uint64_t image_config_info::get_virtual_memory_threshold() const
+{
+ return virtual_memory_threshold_;
+}
+
+//Returns process affinity mask
+uint64_t image_config_info::get_process_affinity_mask() const
+{
+ return process_affinity_mask_;
+}
+
+//Returns process heap flags
+uint32_t image_config_info::get_process_heap_flags() const
+{
+ return process_heap_flags_;
+}
+
+//Returns service pack version (CSDVersion)
+uint16_t image_config_info::get_service_pack_version() const
+{
+ return service_pack_version_;
+}
+
+//Returns VA of edit list (reserved by system)
+uint64_t image_config_info::get_edit_list_va() const
+{
+ return edit_list_va_;
+}
+
+//Returns a pointer to a cookie that is used by Visual C++ or GS implementation
+uint64_t image_config_info::get_security_cookie_va() const
+{
+ return security_cookie_va_;
+}
+
+//Returns VA of the sorted table of RVAs of each valid, unique handler in the image
+uint64_t image_config_info::get_se_handler_table_va() const
+{
+ return se_handler_table_va_;
+}
+
+//Returns the count of unique handlers in the table
+uint64_t image_config_info::get_se_handler_count() const
+{
+ return se_handler_count_;
+}
+
+//Returns SE Handler RVA list
+const image_config_info::se_handler_list& image_config_info::get_se_handler_rvas() const
+{
+ return se_handlers_;
+}
+
+//Returns Lock Prefix RVA list
+const image_config_info::lock_prefix_rva_list& image_config_info::get_lock_prefix_rvas() const
+{
+ return lock_prefixes_;
+}
+
+//Adds SE Handler RVA to list
+void image_config_info::add_se_handler_rva(uint32_t rva)
+{
+ se_handlers_.push_back(rva);
+}
+
+//Clears SE Handler list
+void image_config_info::clear_se_handler_list()
+{
+ se_handlers_.clear();
+}
+
+//Adds Lock Prefix RVA to list
+void image_config_info::add_lock_prefix_rva(uint32_t rva)
+{
+ lock_prefixes_.push_back(rva);
+}
+
+//Clears Lock Prefix list
+void image_config_info::clear_lock_prefix_list()
+{
+ lock_prefixes_.clear();
+}
+
+//Sets the date and time stamp value
+void image_config_info::set_time_stamp(uint32_t time_stamp)
+{
+ time_stamp_ = time_stamp;
+}
+
+//Sets major version number
+void image_config_info::set_major_version(uint16_t major_version)
+{
+ major_version_ = major_version;
+}
+
+//Sets minor version number
+void image_config_info::set_minor_version(uint16_t minor_version)
+{
+ minor_version_ = minor_version;
+}
+
+//Sets clear global flags
+void image_config_info::set_global_flags_clear(uint32_t global_flags_clear)
+{
+ global_flags_clear_ = global_flags_clear;
+}
+
+//Sets set global flags
+void image_config_info::set_global_flags_set(uint32_t global_flags_set)
+{
+ global_flags_set_ = global_flags_set;
+}
+
+//Sets critical section default timeout
+void image_config_info::set_critical_section_default_timeout(uint32_t critical_section_default_timeout)
+{
+ critical_section_default_timeout_ = critical_section_default_timeout;
+}
+
+//Sets the size of the minimum block that
+//must be freed before it is freed (de-committed), in bytes
+void image_config_info::set_decommit_free_block_threshold(uint64_t decommit_free_block_threshold)
+{
+ decommit_free_block_threshold_ = decommit_free_block_threshold;
+}
+
+//Sets the size of the minimum total memory
+//that must be freed in the process heap before it is freed (de-committed), in bytes
+void image_config_info::set_decommit_total_free_threshold(uint64_t decommit_total_free_threshold)
+{
+ decommit_total_free_threshold_ = decommit_total_free_threshold;
+}
+
+//Sets VA of a list of addresses where the LOCK prefix is used
+//If you rebuild this list, VA will be re-assigned automatically
+void image_config_info::set_lock_prefix_table_va(uint64_t lock_prefix_table_va)
+{
+ lock_prefix_table_va_ = lock_prefix_table_va;
+}
+
+//Sets the maximum allocation size, in bytes
+void image_config_info::set_max_allocation_size(uint64_t max_allocation_size)
+{
+ max_allocation_size_ = max_allocation_size;
+}
+
+//Sets the maximum block size that can be allocated from heap segments, in bytes
+void image_config_info::set_virtual_memory_threshold(uint64_t virtual_memory_threshold)
+{
+ virtual_memory_threshold_ = virtual_memory_threshold;
+}
+
+//Sets process affinity mask
+void image_config_info::set_process_affinity_mask(uint64_t process_affinity_mask)
+{
+ process_affinity_mask_ = process_affinity_mask;
+}
+
+//Sets process heap flags
+void image_config_info::set_process_heap_flags(uint32_t process_heap_flags)
+{
+ process_heap_flags_ = process_heap_flags;
+}
+
+//Sets service pack version (CSDVersion)
+void image_config_info::set_service_pack_version(uint16_t service_pack_version)
+{
+ service_pack_version_ = service_pack_version;
+}
+
+//Sets VA of edit list (reserved by system)
+void image_config_info::set_edit_list_va(uint64_t edit_list_va)
+{
+ edit_list_va_ = edit_list_va;
+}
+
+//Sets a pointer to a cookie that is used by Visual C++ or GS implementation
+void image_config_info::set_security_cookie_va(uint64_t security_cookie_va)
+{
+ security_cookie_va_ = security_cookie_va;
+}
+
+//Sets VA of the sorted table of RVAs of each valid, unique handler in the image
+//If you rebuild this list, VA will be re-assigned automatically
+void image_config_info::set_se_handler_table_va(uint64_t se_handler_table_va)
+{
+ se_handler_table_va_ = se_handler_table_va;
+}
+
+//Returns SE Handler RVA list
+image_config_info::se_handler_list& image_config_info::get_se_handler_rvas()
+{
+ return se_handlers_;
+}
+
+//Returns Lock Prefix RVA list
+image_config_info::lock_prefix_rva_list& image_config_info::get_lock_prefix_rvas()
+{
+ return lock_prefixes_;
+}
+
+//Returns image config info
+//If image does not have config info, throws an exception
+const image_config_info get_image_config(const pe_base& pe)
+{
+ return pe.get_pe_type() == pe_type_32
+ ? get_image_config_base<pe_types_class_32>(pe)
+ : get_image_config_base<pe_types_class_64>(pe);
+}
+
+//Image config rebuilder
+const image_directory rebuild_image_config(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start, bool write_se_handlers, bool write_lock_prefixes, bool save_to_pe_header, bool auto_strip_last_section)
+{
+ return pe.get_pe_type() == pe_type_32
+ ? rebuild_image_config_base<pe_types_class_32>(pe, info, image_config_section, offset_from_section_start, write_se_handlers, write_lock_prefixes, save_to_pe_header, auto_strip_last_section)
+ : rebuild_image_config_base<pe_types_class_64>(pe, info, image_config_section, offset_from_section_start, write_se_handlers, write_lock_prefixes, save_to_pe_header, auto_strip_last_section);
+}
+
+
+//Returns image config info
+//If image does not have config info, throws an exception
+template<typename PEClassType>
+const image_config_info get_image_config_base(const pe_base& pe)
+{
+ //Check if image has config directory
+ if(!pe.has_config())
+ throw pe_exception("Image does not have load config directory", pe_exception::directory_does_not_exist);
+
+ //Get load config structure
+ typename PEClassType::ConfigStruct config_info = pe.section_data_from_rva<typename PEClassType::ConfigStruct>(pe.get_directory_rva(image_directory_entry_load_config), section_data_virtual);
+
+ //Check size of config directory
+ if(config_info.Size != sizeof(config_info))
+ throw pe_exception("Incorrect (or old) load config directory", pe_exception::incorrect_config_directory);
+
+ //Fill return structure
+ image_config_info ret(config_info);
+
+ //Check possible overflow
+ if(config_info.SEHandlerCount >= pe_utils::max_dword / sizeof(uint32_t)
+ || config_info.SEHandlerTable >= static_cast<typename PEClassType::BaseSize>(-1) - config_info.SEHandlerCount * sizeof(uint32_t))
+ throw pe_exception("Incorrect load config directory", pe_exception::incorrect_config_directory);
+
+ //Read sorted SE handler RVA list (if any)
+ for(typename PEClassType::BaseSize i = 0; i != config_info.SEHandlerCount; ++i)
+ ret.add_se_handler_rva(pe.section_data_from_va<uint32_t>(static_cast<typename PEClassType::BaseSize>(config_info.SEHandlerTable + i * sizeof(uint32_t))));
+
+ if(config_info.LockPrefixTable)
+ {
+ //Read Lock Prefix VA list (if any)
+ unsigned long current = 0;
+ while(true)
+ {
+ typename PEClassType::BaseSize lock_prefix_va = pe.section_data_from_va<typename PEClassType::BaseSize>(static_cast<typename PEClassType::BaseSize>(config_info.LockPrefixTable + current * sizeof(typename PEClassType::BaseSize)));
+ if(!lock_prefix_va)
+ break;
+
+ ret.add_lock_prefix_rva(pe.va_to_rva(lock_prefix_va));
+
+ ++current;
+ }
+ }
+
+ return ret;
+}
+
+//Image config directory rebuilder
+//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped
+//If write_se_handlers = true, SE Handlers list will be written just after image config directory structure
+//If write_lock_prefixes = true, Lock Prefixes address list will be written just after image config directory structure
+template<typename PEClassType>
+const image_directory rebuild_image_config_base(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start, bool write_se_handlers, bool write_lock_prefixes, bool save_to_pe_header, bool auto_strip_last_section)
+{
+ //Check that image_config_section is attached to this PE image
+ if(!pe.section_attached(image_config_section))
+ throw pe_exception("Image Config section must be attached to PE file", pe_exception::section_is_not_attached);
+
+ uint32_t alignment = pe_utils::align_up(offset_from_section_start, sizeof(typename PEClassType::BaseSize)) - offset_from_section_start;
+
+ uint32_t needed_size = sizeof(typename PEClassType::ConfigStruct); //Calculate needed size for Image Config table
+
+ uint32_t image_config_data_pos = offset_from_section_start + alignment;
+
+ uint32_t current_pos_of_se_handlers = 0;
+ uint32_t current_pos_of_lock_prefixes = 0;
+
+ if(write_se_handlers)
+ {
+ current_pos_of_se_handlers = needed_size + image_config_data_pos;
+ needed_size += static_cast<uint32_t>(info.get_se_handler_rvas().size()) * sizeof(uint32_t); //RVAs of SE Handlers
+ }
+
+ if(write_lock_prefixes)
+ {
+ current_pos_of_lock_prefixes = needed_size + image_config_data_pos;
+ needed_size += static_cast<uint32_t>((info.get_lock_prefix_rvas().size() + 1) * sizeof(typename PEClassType::BaseSize)); //VAs of Lock Prefixes (and ending null element)
+ }
+
+ //Check if image_config_section is last one. If it's not, check if there's enough place for Image Config data
+ if(&image_config_section != &*(pe.get_image_sections().end() - 1) &&
+ (image_config_section.empty() || pe_utils::align_up(image_config_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + image_config_data_pos))
+ throw pe_exception("Insufficient space for TLS directory", pe_exception::insufficient_space);
+
+ std::string& raw_data = image_config_section.get_raw_data();
+
+ //This will be done only if image_config_section is the last section of image or for section with unaligned raw length of data
+ if(raw_data.length() < needed_size + image_config_data_pos)
+ raw_data.resize(needed_size + image_config_data_pos); //Expand section raw data
+
+ //Create and fill Image Config structure
+ typename PEClassType::ConfigStruct image_config_section_struct = {0};
+ image_config_section_struct.Size = sizeof(image_config_section_struct);
+ image_config_section_struct.TimeDateStamp = info.get_time_stamp();
+ image_config_section_struct.MajorVersion = info.get_major_version();
+ image_config_section_struct.MinorVersion = info.get_minor_version();
+ image_config_section_struct.GlobalFlagsClear = info.get_global_flags_clear();
+ image_config_section_struct.GlobalFlagsSet = info.get_global_flags_set();
+ image_config_section_struct.CriticalSectionDefaultTimeout = info.get_critical_section_default_timeout();
+ image_config_section_struct.DeCommitFreeBlockThreshold = static_cast<typename PEClassType::BaseSize>(info.get_decommit_free_block_threshold());
+ image_config_section_struct.DeCommitTotalFreeThreshold = static_cast<typename PEClassType::BaseSize>(info.get_decommit_total_free_threshold());
+ image_config_section_struct.MaximumAllocationSize = static_cast<typename PEClassType::BaseSize>(info.get_max_allocation_size());
+ image_config_section_struct.VirtualMemoryThreshold = static_cast<typename PEClassType::BaseSize>(info.get_virtual_memory_threshold());
+ image_config_section_struct.ProcessHeapFlags = info.get_process_heap_flags();
+ image_config_section_struct.ProcessAffinityMask = static_cast<typename PEClassType::BaseSize>(info.get_process_affinity_mask());
+ image_config_section_struct.CSDVersion = info.get_service_pack_version();
+ image_config_section_struct.EditList = static_cast<typename PEClassType::BaseSize>(info.get_edit_list_va());
+ image_config_section_struct.SecurityCookie = static_cast<typename PEClassType::BaseSize>(info.get_security_cookie_va());
+ image_config_section_struct.SEHandlerCount = static_cast<typename PEClassType::BaseSize>(info.get_se_handler_rvas().size());
+
+
+ if(write_se_handlers)
+ {
+ if(info.get_se_handler_rvas().empty())
+ {
+ write_se_handlers = false;
+ image_config_section_struct.SEHandlerTable = 0;
+ }
+ else
+ {
+ typename PEClassType::BaseSize va;
+ pe.rva_to_va(pe.rva_from_section_offset(image_config_section, current_pos_of_se_handlers), va);
+ image_config_section_struct.SEHandlerTable = va;
+ }
+ }
+ else
+ {
+ image_config_section_struct.SEHandlerTable = static_cast<typename PEClassType::BaseSize>(info.get_se_handler_table_va());
+ }
+
+ if(write_lock_prefixes)
+ {
+ if(info.get_lock_prefix_rvas().empty())
+ {
+ write_lock_prefixes = false;
+ image_config_section_struct.LockPrefixTable = 0;
+ }
+ else
+ {
+ typename PEClassType::BaseSize va;
+ pe.rva_to_va(pe.rva_from_section_offset(image_config_section, current_pos_of_lock_prefixes), va);
+ image_config_section_struct.LockPrefixTable = va;
+ }
+ }
+ else
+ {
+ image_config_section_struct.LockPrefixTable = static_cast<typename PEClassType::BaseSize>(info.get_lock_prefix_table_va());
+ }
+
+ //Write image config section
+ memcpy(&raw_data[image_config_data_pos], &image_config_section_struct, sizeof(image_config_section_struct));
+
+ if(write_se_handlers)
+ {
+ //Sort SE Handlers list
+ image_config_info::se_handler_list sorted_list = info.get_se_handler_rvas();
+ std::sort(sorted_list.begin(), sorted_list.end());
+
+ //Write SE Handlers table
+ for(image_config_info::se_handler_list::const_iterator it = sorted_list.begin(); it != sorted_list.end(); ++it)
+ {
+ uint32_t se_handler_rva = *it;
+ memcpy(&raw_data[current_pos_of_se_handlers], &se_handler_rva, sizeof(se_handler_rva));
+ current_pos_of_se_handlers += sizeof(se_handler_rva);
+ }
+ }
+
+ if(write_lock_prefixes)
+ {
+ //Write Lock Prefixes VA list
+ for(image_config_info::lock_prefix_rva_list::const_iterator it = info.get_lock_prefix_rvas().begin(); it != info.get_lock_prefix_rvas().end(); ++it)
+ {
+ typename PEClassType::BaseSize lock_prefix_va;
+ pe.rva_to_va(*it, lock_prefix_va);
+ memcpy(&raw_data[current_pos_of_lock_prefixes], &lock_prefix_va, sizeof(lock_prefix_va));
+ current_pos_of_lock_prefixes += sizeof(lock_prefix_va);
+ }
+
+ {
+ //Ending null VA
+ typename PEClassType::BaseSize lock_prefix_va = 0;
+ memcpy(&raw_data[current_pos_of_lock_prefixes], &lock_prefix_va, sizeof(lock_prefix_va));
+ }
+ }
+
+ //Adjust section raw and virtual sizes
+ pe.recalculate_section_sizes(image_config_section, auto_strip_last_section);
+
+ image_directory ret(pe.rva_from_section_offset(image_config_section, image_config_data_pos), sizeof(typename PEClassType::ConfigStruct));
+
+ //If auto-rewrite of PE headers is required
+ if(save_to_pe_header)
+ {
+ pe.set_directory_rva(image_directory_entry_load_config, ret.get_rva());
+ pe.set_directory_size(image_directory_entry_load_config, ret.get_size());
+ }
+
+ return ret;
+}
+
+}
diff --git a/tools/pe_bliss/pe_load_config.h b/tools/pe_bliss/pe_load_config.h
new file mode 100644
index 0000000000..cb24072de7
--- /dev/null
+++ b/tools/pe_bliss/pe_load_config.h
@@ -0,0 +1,184 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <vector>
+#include "pe_structures.h"
+#include "pe_base.h"
+#include "pe_directory.h"
+
+namespace pe_bliss
+{
+//Class representing image configuration information
+class image_config_info
+{
+public:
+ typedef std::vector<uint32_t> se_handler_list;
+ typedef std::vector<uint32_t> lock_prefix_rva_list;
+
+public:
+ //Default constructor
+ image_config_info();
+ //Constructors from PE structures (no checks)
+ template<typename ConfigStructure>
+ explicit image_config_info(const ConfigStructure& info);
+
+ //Returns the date and time stamp value
+ uint32_t get_time_stamp() const;
+ //Returns major version number
+ uint16_t get_major_version() const;
+ //Returns minor version number
+ uint16_t get_minor_version() const;
+ //Returns clear global flags
+ uint32_t get_global_flags_clear() const;
+ //Returns set global flags
+ uint32_t get_global_flags_set() const;
+ //Returns critical section default timeout
+ uint32_t get_critical_section_default_timeout() const;
+ //Get the size of the minimum block that
+ //must be freed before it is freed (de-committed), in bytes
+ uint64_t get_decommit_free_block_threshold() const;
+ //Returns the size of the minimum total memory
+ //that must be freed in the process heap before it is freed (de-committed), in bytes
+ uint64_t get_decommit_total_free_threshold() const;
+ //Returns VA of a list of addresses where the LOCK prefix is used
+ uint64_t get_lock_prefix_table_va() const;
+ //Returns the maximum allocation size, in bytes
+ uint64_t get_max_allocation_size() const;
+ //Returns the maximum block size that can be allocated from heap segments, in bytes
+ uint64_t get_virtual_memory_threshold() const;
+ //Returns process affinity mask
+ uint64_t get_process_affinity_mask() const;
+ //Returns process heap flags
+ uint32_t get_process_heap_flags() const;
+ //Returns service pack version (CSDVersion)
+ uint16_t get_service_pack_version() const;
+ //Returns VA of edit list (reserved by system)
+ uint64_t get_edit_list_va() const;
+ //Returns a pointer to a cookie that is used by Visual C++ or GS implementation
+ uint64_t get_security_cookie_va() const;
+ //Returns VA of the sorted table of RVAs of each valid, unique handler in the image
+ uint64_t get_se_handler_table_va() const;
+ //Returns the count of unique handlers in the table
+ uint64_t get_se_handler_count() const;
+
+ //Returns SE Handler RVA list
+ const se_handler_list& get_se_handler_rvas() const;
+
+ //Returns Lock Prefix RVA list
+ const lock_prefix_rva_list& get_lock_prefix_rvas() const;
+
+public: //These functions do not change everything inside image, they are used by PE class
+ //Also you can use these functions to rebuild image config directory
+
+ //Adds SE Handler RVA to list
+ void add_se_handler_rva(uint32_t rva);
+ //Clears SE Handler list
+ void clear_se_handler_list();
+
+ //Adds Lock Prefix RVA to list
+ void add_lock_prefix_rva(uint32_t rva);
+ //Clears Lock Prefix list
+ void clear_lock_prefix_list();
+
+ //Sets the date and time stamp value
+ void set_time_stamp(uint32_t time_stamp);
+ //Sets major version number
+ void set_major_version(uint16_t major_version);
+ //Sets minor version number
+ void set_minor_version(uint16_t minor_version);
+ //Sets clear global flags
+ void set_global_flags_clear(uint32_t global_flags_clear);
+ //Sets set global flags
+ void set_global_flags_set(uint32_t global_flags_set);
+ //Sets critical section default timeout
+ void set_critical_section_default_timeout(uint32_t critical_section_default_timeout);
+ //Sets the size of the minimum block that
+ //must be freed before it is freed (de-committed), in bytes
+ void set_decommit_free_block_threshold(uint64_t decommit_free_block_threshold);
+ //Sets the size of the minimum total memory
+ //that must be freed in the process heap before it is freed (de-committed), in bytes
+ void set_decommit_total_free_threshold(uint64_t decommit_total_free_threshold);
+ //Sets VA of a list of addresses where the LOCK prefix is used
+ //If you rebuild this list, VA will be re-assigned automatically
+ void set_lock_prefix_table_va(uint64_t lock_prefix_table_va);
+ //Sets the maximum allocation size, in bytes
+ void set_max_allocation_size(uint64_t max_allocation_size);
+ //Sets the maximum block size that can be allocated from heap segments, in bytes
+ void set_virtual_memory_threshold(uint64_t virtual_memory_threshold);
+ //Sets process affinity mask
+ void set_process_affinity_mask(uint64_t process_affinity_mask);
+ //Sets process heap flags
+ void set_process_heap_flags(uint32_t process_heap_flags);
+ //Sets service pack version (CSDVersion)
+ void set_service_pack_version(uint16_t service_pack_version);
+ //Sets VA of edit list (reserved by system)
+ void set_edit_list_va(uint64_t edit_list_va);
+ //Sets a pointer to a cookie that is used by Visual C++ or GS implementation
+ void set_security_cookie_va(uint64_t security_cookie_va);
+ //Sets VA of the sorted table of RVAs of each valid, unique handler in the image
+ //If you rebuild this list, VA will be re-assigned automatically
+ void set_se_handler_table_va(uint64_t se_handler_table_va);
+
+ //Returns SE Handler RVA list
+ se_handler_list& get_se_handler_rvas();
+
+ //Returns Lock Prefix RVA list
+ lock_prefix_rva_list& get_lock_prefix_rvas();
+
+private:
+ uint32_t time_stamp_;
+ uint16_t major_version_, minor_version_;
+ uint32_t global_flags_clear_, global_flags_set_;
+ uint32_t critical_section_default_timeout_;
+ uint64_t decommit_free_block_threshold_, decommit_total_free_threshold_;
+ uint64_t lock_prefix_table_va_;
+ uint64_t max_allocation_size_;
+ uint64_t virtual_memory_threshold_;
+ uint64_t process_affinity_mask_;
+ uint32_t process_heap_flags_;
+ uint16_t service_pack_version_;
+ uint64_t edit_list_va_;
+ uint64_t security_cookie_va_;
+ uint64_t se_handler_table_va_;
+ uint64_t se_handler_count_;
+
+ se_handler_list se_handlers_;
+ lock_prefix_rva_list lock_prefixes_;
+};
+
+//Returns image config info
+//If image does not have config info, throws an exception
+const image_config_info get_image_config(const pe_base& pe);
+
+template<typename PEClassType>
+const image_config_info get_image_config_base(const pe_base& pe);
+
+
+//Image config directory rebuilder
+//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped
+//If write_se_handlers = true, SE Handlers list will be written just after image config directory structure
+//If write_lock_prefixes = true, Lock Prefixes address list will be written just after image config directory structure
+const image_directory rebuild_image_config(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start = 0, bool write_se_handlers = true, bool write_lock_prefixes = true, bool save_to_pe_header = true, bool auto_strip_last_section = true);
+
+template<typename PEClassType>
+const image_directory rebuild_image_config_base(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start = 0, bool write_se_handlers = true, bool write_lock_prefixes = true, bool save_to_pe_header = true, bool auto_strip_last_section = true);
+}
diff --git a/tools/pe_bliss/pe_properties.cpp b/tools/pe_bliss/pe_properties.cpp
new file mode 100644
index 0000000000..8d1c2eac43
--- /dev/null
+++ b/tools/pe_bliss/pe_properties.cpp
@@ -0,0 +1,41 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "pe_properties.h"
+
+namespace pe_bliss
+{
+//Destructor
+pe_properties::~pe_properties()
+{}
+
+//Clears PE characteristics flag
+void pe_properties::clear_characteristics_flags(uint16_t flags)
+{
+ set_characteristics(get_characteristics() & ~flags);
+}
+
+//Sets PE characteristics flag
+void pe_properties::set_characteristics_flags(uint16_t flags)
+{
+ set_characteristics(get_characteristics() | flags);
+}
+}
diff --git a/tools/pe_bliss/pe_properties.h b/tools/pe_bliss/pe_properties.h
new file mode 100644
index 0000000000..1db163e8b1
--- /dev/null
+++ b/tools/pe_bliss/pe_properties.h
@@ -0,0 +1,236 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <memory>
+#include "pe_structures.h"
+
+namespace pe_bliss
+{
+class pe_properties
+{
+public: //Constructors
+ virtual std::auto_ptr<pe_properties> duplicate() const = 0;
+
+ //Fills properly PE structures
+ virtual void create_pe(uint32_t section_alignment, uint16_t subsystem) = 0;
+
+public:
+ //Destructor
+ virtual ~pe_properties();
+
+
+public: //DIRECTORIES
+ //Returns true if directory exists
+ virtual bool directory_exists(uint32_t id) const = 0;
+
+ //Removes directory
+ virtual void remove_directory(uint32_t id) = 0;
+
+ //Returns directory RVA
+ virtual uint32_t get_directory_rva(uint32_t id) const = 0;
+ //Returns directory size
+ virtual uint32_t get_directory_size(uint32_t id) const = 0;
+
+ //Sets directory RVA (just a value of PE header, no moving occurs)
+ virtual void set_directory_rva(uint32_t id, uint32_t rva) = 0;
+ //Sets directory size (just a value of PE header, no moving occurs)
+ virtual void set_directory_size(uint32_t id, uint32_t size) = 0;
+
+ //Strips only zero DATA_DIRECTORY entries to count = min_count
+ //Returns resulting number of data directories
+ //strip_iat_directory - if true, even not empty IAT directory will be stripped
+ virtual uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true) = 0;
+
+
+public: //IMAGE
+ //Returns PE type of this image
+ virtual pe_type get_pe_type() const = 0;
+
+
+public: //PE HEADER
+ //Returns image base for PE32 and PE64 respectively
+ virtual uint32_t get_image_base_32() const = 0;
+ virtual uint64_t get_image_base_64() const = 0;
+
+ //Sets new image base for PE32
+ virtual void set_image_base(uint32_t base) = 0;
+ //Sets new image base for PE32/PE+
+ virtual void set_image_base_64(uint64_t base) = 0;
+
+ //Returns image entry point
+ virtual uint32_t get_ep() const = 0;
+ //Sets image entry point
+ virtual void set_ep(uint32_t new_ep) = 0;
+
+ //Returns file alignment
+ virtual uint32_t get_file_alignment() const = 0;
+ //Returns section alignment
+ virtual uint32_t get_section_alignment() const = 0;
+
+ //Sets heap size commit for PE32 and PE64 respectively
+ virtual void set_heap_size_commit(uint32_t size) = 0;
+ virtual void set_heap_size_commit(uint64_t size) = 0;
+ //Sets heap size reserve for PE32 and PE64 respectively
+ virtual void set_heap_size_reserve(uint32_t size) = 0;
+ virtual void set_heap_size_reserve(uint64_t size) = 0;
+ //Sets stack size commit for PE32 and PE64 respectively
+ virtual void set_stack_size_commit(uint32_t size) = 0;
+ virtual void set_stack_size_commit(uint64_t size) = 0;
+ //Sets stack size reserve for PE32 and PE64 respectively
+ virtual void set_stack_size_reserve(uint32_t size) = 0;
+ virtual void set_stack_size_reserve(uint64_t size) = 0;
+
+ //Returns heap size commit for PE32 and PE64 respectively
+ virtual uint32_t get_heap_size_commit_32() const = 0;
+ virtual uint64_t get_heap_size_commit_64() const = 0;
+ //Returns heap size reserve for PE32 and PE64 respectively
+ virtual uint32_t get_heap_size_reserve_32() const = 0;
+ virtual uint64_t get_heap_size_reserve_64() const = 0;
+ //Returns stack size commit for PE32 and PE64 respectively
+ virtual uint32_t get_stack_size_commit_32() const = 0;
+ virtual uint64_t get_stack_size_commit_64() const = 0;
+ //Returns stack size reserve for PE32 and PE64 respectively
+ virtual uint32_t get_stack_size_reserve_32() const = 0;
+ virtual uint64_t get_stack_size_reserve_64() const = 0;
+
+ //Returns virtual size of image
+ virtual uint32_t get_size_of_image() const = 0;
+
+ //Returns number of RVA and sizes (number of DATA_DIRECTORY entries)
+ virtual uint32_t get_number_of_rvas_and_sizes() const = 0;
+ //Sets number of RVA and sizes (number of DATA_DIRECTORY entries)
+ virtual void set_number_of_rvas_and_sizes(uint32_t number) = 0;
+
+ //Returns PE characteristics
+ virtual uint16_t get_characteristics() const = 0;
+ //Sets PE characteristics
+ virtual void set_characteristics(uint16_t ch) = 0;
+
+ //Clears PE characteristics flag
+ void clear_characteristics_flags(uint16_t flags);
+ //Sets PE characteristics flag
+ void set_characteristics_flags(uint16_t flags);
+
+ //Returns size of headers
+ virtual uint32_t get_size_of_headers() const = 0;
+
+ //Returns subsystem
+ virtual uint16_t get_subsystem() const = 0;
+
+ //Sets subsystem
+ virtual void set_subsystem(uint16_t subsystem) = 0;
+
+ //Returns size of optional header
+ virtual uint16_t get_size_of_optional_header() const = 0;
+
+ //Returns PE signature
+ virtual uint32_t get_pe_signature() const = 0;
+
+ //Returns PE magic value
+ virtual uint32_t get_magic() const = 0;
+
+ //Returns checksum of PE file from header
+ virtual uint32_t get_checksum() const = 0;
+
+ //Sets checksum of PE file
+ virtual void set_checksum(uint32_t checksum) = 0;
+
+ //Returns timestamp of PE file from header
+ virtual uint32_t get_time_date_stamp() const = 0;
+
+ //Sets timestamp of PE file
+ virtual void set_time_date_stamp(uint32_t timestamp) = 0;
+
+ //Returns Machine field value of PE file from header
+ virtual uint16_t get_machine() const = 0;
+
+ //Sets Machine field value of PE file
+ virtual void set_machine(uint16_t machine) = 0;
+
+ //Returns DLL Characteristics
+ virtual uint16_t get_dll_characteristics() const = 0;
+
+ //Sets DLL Characteristics
+ virtual void set_dll_characteristics(uint16_t characteristics) = 0;
+
+ //Sets required operation system version
+ virtual void set_os_version(uint16_t major, uint16_t minor) = 0;
+
+ //Returns required operation system version (minor word)
+ virtual uint16_t get_minor_os_version() const = 0;
+
+ //Returns required operation system version (major word)
+ virtual uint16_t get_major_os_version() const = 0;
+
+ //Sets required subsystem version
+ virtual void set_subsystem_version(uint16_t major, uint16_t minor) = 0;
+
+ //Returns required subsystem version (minor word)
+ virtual uint16_t get_minor_subsystem_version() const = 0;
+
+ //Returns required subsystem version (major word)
+ virtual uint16_t get_major_subsystem_version() const = 0;
+
+public: //ADDRESS CONVERTIONS
+ //Virtual Address (VA) to Relative Virtual Address (RVA) convertions
+ //for PE32 and PE64 respectively
+ //bound_check checks integer overflow
+ virtual uint32_t va_to_rva(uint32_t va, bool bound_check = true) const = 0;
+ virtual uint32_t va_to_rva(uint64_t va, bool bound_check = true) const = 0;
+
+ //Relative Virtual Address (RVA) to Virtual Address (VA) convertions
+ //for PE32 and PE64 respectively
+ virtual uint32_t rva_to_va_32(uint32_t rva) const = 0;
+ virtual uint64_t rva_to_va_64(uint32_t rva) const = 0;
+
+
+public: //SECTIONS
+ //Returns number of sections
+ virtual uint16_t get_number_of_sections() const = 0;
+
+public:
+ //Sets number of sections
+ virtual void set_number_of_sections(uint16_t number) = 0;
+ //Sets virtual size of image
+ virtual void set_size_of_image(uint32_t size) = 0;
+ //Sets size of headers
+ virtual void set_size_of_headers(uint32_t size) = 0;
+ //Sets size of optional headers
+ virtual void set_size_of_optional_header(uint16_t size) = 0;
+ //Returns nt headers data pointer
+ virtual char* get_nt_headers_ptr() = 0;
+ //Returns nt headers data pointer
+ virtual const char* get_nt_headers_ptr() const = 0;
+ //Returns size of NT header
+ virtual uint32_t get_sizeof_nt_header() const = 0;
+ //Returns size of optional headers
+ virtual uint32_t get_sizeof_opt_headers() const = 0;
+ //Sets file alignment (no checks)
+ virtual void set_file_alignment_unchecked(uint32_t alignment) = 0;
+ //Sets base of code
+ virtual void set_base_of_code(uint32_t base) = 0;
+ //Returns base of code
+ virtual uint32_t get_base_of_code() const = 0;
+ //Returns needed PE magic for PE or PE+ (from template parameters)
+ virtual uint32_t get_needed_magic() const = 0;
+};
+}
diff --git a/tools/pe_bliss/pe_properties_generic.cpp b/tools/pe_bliss/pe_properties_generic.cpp
new file mode 100644
index 0000000000..bcf6f2047d
--- /dev/null
+++ b/tools/pe_bliss/pe_properties_generic.cpp
@@ -0,0 +1,645 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <string.h>
+#include "pe_properties_generic.h"
+#include "pe_exception.h"
+#include "utils.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//Constructor
+template<typename PEClassType>
+std::auto_ptr<pe_properties> pe_properties_generic<PEClassType>::duplicate() const
+{
+ return std::auto_ptr<pe_properties>(new pe_properties_generic<PEClassType>(*this));
+}
+
+//Fills properly PE structures
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::create_pe(uint32_t section_alignment, uint16_t subsystem)
+{
+ memset(&nt_headers_, 0, sizeof(nt_headers_));
+ nt_headers_.Signature = 0x4550; //"PE"
+ nt_headers_.FileHeader.Machine = 0x14C; //i386
+ nt_headers_.FileHeader.SizeOfOptionalHeader = sizeof(nt_headers_.OptionalHeader);
+ nt_headers_.OptionalHeader.Magic = PEClassType::Id;
+ nt_headers_.OptionalHeader.ImageBase = 0x400000;
+ nt_headers_.OptionalHeader.SectionAlignment = section_alignment;
+ nt_headers_.OptionalHeader.FileAlignment = 0x200;
+ nt_headers_.OptionalHeader.SizeOfHeaders = 1024;
+ nt_headers_.OptionalHeader.Subsystem = subsystem;
+ nt_headers_.OptionalHeader.SizeOfHeapReserve = 0x100000;
+ nt_headers_.OptionalHeader.SizeOfHeapCommit = 0x1000;
+ nt_headers_.OptionalHeader.SizeOfStackReserve = 0x100000;
+ nt_headers_.OptionalHeader.SizeOfStackCommit = 0x1000;
+ nt_headers_.OptionalHeader.NumberOfRvaAndSizes = 0x10;
+}
+
+//Duplicate
+template<typename PEClassType>
+pe_properties_generic<PEClassType>::~pe_properties_generic()
+{}
+
+//Returns true if directory exists
+template<typename PEClassType>
+bool pe_properties_generic<PEClassType>::directory_exists(uint32_t id) const
+{
+ return (nt_headers_.OptionalHeader.NumberOfRvaAndSizes - 1) >= id &&
+ nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress;
+}
+
+//Removes directory
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::remove_directory(uint32_t id)
+{
+ if(directory_exists(id))
+ {
+ nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress = 0;
+ nt_headers_.OptionalHeader.DataDirectory[id].Size = 0;
+
+ if(id == image_directory_entry_basereloc)
+ {
+ set_characteristics_flags(image_file_relocs_stripped);
+ set_dll_characteristics(get_dll_characteristics() & ~image_dllcharacteristics_dynamic_base);
+ }
+ else if(id == image_directory_entry_export)
+ {
+ clear_characteristics_flags(image_file_dll);
+ }
+ }
+}
+
+//Returns directory RVA
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_directory_rva(uint32_t id) const
+{
+ //Check if directory exists
+ if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id)
+ throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist);
+
+ return nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress;
+}
+
+//Returns directory size
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_directory_rva(uint32_t id, uint32_t va)
+{
+ //Check if directory exists
+ if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id)
+ throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist);
+
+ nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress = va;
+}
+
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_directory_size(uint32_t id, uint32_t size)
+{
+ //Check if directory exists
+ if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id)
+ throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist);
+
+ nt_headers_.OptionalHeader.DataDirectory[id].Size = size;
+}
+
+//Returns directory size
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_directory_size(uint32_t id) const
+{
+ //Check if directory exists
+ if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id)
+ throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist);
+
+ return nt_headers_.OptionalHeader.DataDirectory[id].Size;
+}
+
+//Strips only zero DATA_DIRECTORY entries to count = min_count
+//Returns resulting number of data directories
+//strip_iat_directory - if true, even not empty IAT directory will be stripped
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::strip_data_directories(uint32_t min_count, bool strip_iat_directory)
+{
+ int i = nt_headers_.OptionalHeader.NumberOfRvaAndSizes - 1;
+
+ //Enumerate all data directories from the end
+ for(; i >= 0; i--)
+ {
+ //If directory exists, break
+ if(nt_headers_.OptionalHeader.DataDirectory[i].VirtualAddress && (static_cast<uint32_t>(i) != image_directory_entry_iat || !strip_iat_directory))
+ break;
+
+ if(i <= static_cast<int>(min_count) - 2)
+ break;
+ }
+
+ if(i == image_numberof_directory_entries - 1)
+ return image_numberof_directory_entries;
+
+ //Return new number of data directories
+ return nt_headers_.OptionalHeader.NumberOfRvaAndSizes = i + 1;
+}
+
+//Returns image base for PE32
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_image_base_32() const
+{
+ return static_cast<uint32_t>(nt_headers_.OptionalHeader.ImageBase);
+}
+
+//Returns image base for PE32/PE64
+template<typename PEClassType>
+uint64_t pe_properties_generic<PEClassType>::get_image_base_64() const
+{
+ return static_cast<uint64_t>(nt_headers_.OptionalHeader.ImageBase);
+}
+
+//Sets new image base
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_image_base(uint32_t base)
+{
+ nt_headers_.OptionalHeader.ImageBase = base;
+}
+
+//Sets new image base
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_image_base_64(uint64_t base)
+{
+ nt_headers_.OptionalHeader.ImageBase = static_cast<typename PEClassType::BaseSize>(base);
+}
+
+//Returns image entry point
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_ep() const
+{
+ return nt_headers_.OptionalHeader.AddressOfEntryPoint;
+}
+
+//Sets image entry point
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_ep(uint32_t new_ep)
+{
+ nt_headers_.OptionalHeader.AddressOfEntryPoint = new_ep;
+}
+
+//Returns file alignment
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_file_alignment() const
+{
+ return nt_headers_.OptionalHeader.FileAlignment;
+}
+
+//Returns section alignment
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_section_alignment() const
+{
+ return nt_headers_.OptionalHeader.SectionAlignment;
+}
+
+//Sets heap size commit for PE32
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_heap_size_commit(uint32_t size)
+{
+ nt_headers_.OptionalHeader.SizeOfHeapCommit = static_cast<typename PEClassType::BaseSize>(size);
+}
+
+//Sets heap size commit for PE32/PE64
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_heap_size_commit(uint64_t size)
+{
+ nt_headers_.OptionalHeader.SizeOfHeapCommit = static_cast<typename PEClassType::BaseSize>(size);
+}
+
+//Sets heap size reserve for PE32
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_heap_size_reserve(uint32_t size)
+{
+ nt_headers_.OptionalHeader.SizeOfHeapReserve = static_cast<typename PEClassType::BaseSize>(size);
+}
+
+//Sets heap size reserve for PE32/PE64
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_heap_size_reserve(uint64_t size)
+{
+ nt_headers_.OptionalHeader.SizeOfHeapReserve = static_cast<typename PEClassType::BaseSize>(size);
+}
+
+//Sets stack size commit for PE32
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_stack_size_commit(uint32_t size)
+{
+ nt_headers_.OptionalHeader.SizeOfStackCommit = static_cast<typename PEClassType::BaseSize>(size);
+}
+
+//Sets stack size commit for PE32/PE64
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_stack_size_commit(uint64_t size)
+{
+ nt_headers_.OptionalHeader.SizeOfStackCommit = static_cast<typename PEClassType::BaseSize>(size);
+}
+
+//Sets stack size reserve for PE32
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_stack_size_reserve(uint32_t size)
+{
+ nt_headers_.OptionalHeader.SizeOfStackReserve = static_cast<typename PEClassType::BaseSize>(size);
+}
+
+//Sets stack size reserve for PE32/PE64
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_stack_size_reserve(uint64_t size)
+{
+ nt_headers_.OptionalHeader.SizeOfStackReserve = static_cast<typename PEClassType::BaseSize>(size);
+}
+
+//Returns heap size commit for PE32
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_heap_size_commit_32() const
+{
+ return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfHeapCommit);
+}
+
+//Returns heap size commit for PE32/PE64
+template<typename PEClassType>
+uint64_t pe_properties_generic<PEClassType>::get_heap_size_commit_64() const
+{
+ return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfHeapCommit);
+}
+
+//Returns heap size reserve for PE32
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_heap_size_reserve_32() const
+{
+ return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfHeapReserve);
+}
+
+//Returns heap size reserve for PE32/PE64
+template<typename PEClassType>
+uint64_t pe_properties_generic<PEClassType>::get_heap_size_reserve_64() const
+{
+ return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfHeapReserve);
+}
+
+//Returns stack size commit for PE32
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_stack_size_commit_32() const
+{
+ return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfStackCommit);
+}
+
+//Returns stack size commit for PE32/PE64
+template<typename PEClassType>
+uint64_t pe_properties_generic<PEClassType>::get_stack_size_commit_64() const
+{
+ return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfStackCommit);
+}
+
+//Returns stack size reserve for PE32
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_stack_size_reserve_32() const
+{
+ return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfStackReserve);
+}
+
+//Returns stack size reserve for PE32/PE64
+template<typename PEClassType>
+uint64_t pe_properties_generic<PEClassType>::get_stack_size_reserve_64() const
+{
+ return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfStackReserve);
+}
+
+//Returns virtual size of image
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_size_of_image() const
+{
+ return nt_headers_.OptionalHeader.SizeOfImage;
+}
+
+//Returns number of RVA and sizes (number of DATA_DIRECTORY entries)
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_number_of_rvas_and_sizes() const
+{
+ return nt_headers_.OptionalHeader.NumberOfRvaAndSizes;
+}
+
+//Sets number of RVA and sizes (number of DATA_DIRECTORY entries)
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_number_of_rvas_and_sizes(uint32_t number)
+{
+ nt_headers_.OptionalHeader.NumberOfRvaAndSizes = number;
+}
+
+//Returns PE characteristics
+template<typename PEClassType>
+uint16_t pe_properties_generic<PEClassType>::get_characteristics() const
+{
+ return nt_headers_.FileHeader.Characteristics;
+}
+
+//Returns checksum of PE file from header
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_checksum() const
+{
+ return nt_headers_.OptionalHeader.CheckSum;
+}
+
+//Sets checksum of PE file
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_checksum(uint32_t checksum)
+{
+ nt_headers_.OptionalHeader.CheckSum = checksum;
+}
+
+//Returns DLL Characteristics
+template<typename PEClassType>
+uint16_t pe_properties_generic<PEClassType>::get_dll_characteristics() const
+{
+ return nt_headers_.OptionalHeader.DllCharacteristics;
+}
+
+//Returns timestamp of PE file from header
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_time_date_stamp() const
+{
+ return nt_headers_.FileHeader.TimeDateStamp;
+}
+
+//Sets timestamp of PE file
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_time_date_stamp(uint32_t timestamp)
+{
+ nt_headers_.FileHeader.TimeDateStamp = timestamp;
+}
+
+//Sets DLL Characteristics
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_dll_characteristics(uint16_t characteristics)
+{
+ nt_headers_.OptionalHeader.DllCharacteristics = characteristics;
+}
+
+//Returns Machine field value of PE file from header
+template<typename PEClassType>
+uint16_t pe_properties_generic<PEClassType>::get_machine() const
+{
+ return nt_headers_.FileHeader.Machine;
+}
+
+//Sets Machine field value of PE file
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_machine(uint16_t machine)
+{
+ nt_headers_.FileHeader.Machine = machine;
+}
+
+//Sets PE characteristics
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_characteristics(uint16_t ch)
+{
+ nt_headers_.FileHeader.Characteristics = ch;
+}
+
+//Returns size of headers
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_size_of_headers() const
+{
+ return nt_headers_.OptionalHeader.SizeOfHeaders;
+}
+
+//Returns subsystem
+template<typename PEClassType>
+uint16_t pe_properties_generic<PEClassType>::get_subsystem() const
+{
+ return nt_headers_.OptionalHeader.Subsystem;
+}
+
+//Sets subsystem
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_subsystem(uint16_t subsystem)
+{
+ nt_headers_.OptionalHeader.Subsystem = subsystem;
+}
+
+//Returns size of optional header
+template<typename PEClassType>
+uint16_t pe_properties_generic<PEClassType>::get_size_of_optional_header() const
+{
+ return nt_headers_.FileHeader.SizeOfOptionalHeader;
+}
+
+//Returns PE signature
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_pe_signature() const
+{
+ return nt_headers_.Signature;
+}
+
+//Returns PE magic value
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_magic() const
+{
+ return nt_headers_.OptionalHeader.Magic;
+}
+
+//Sets required operation system version
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_os_version(uint16_t major, uint16_t minor)
+{
+ nt_headers_.OptionalHeader.MinorOperatingSystemVersion = minor;
+ nt_headers_.OptionalHeader.MajorOperatingSystemVersion = major;
+}
+
+//Returns required operation system version (minor word)
+template<typename PEClassType>
+uint16_t pe_properties_generic<PEClassType>::get_minor_os_version() const
+{
+ return nt_headers_.OptionalHeader.MinorOperatingSystemVersion;
+}
+
+//Returns required operation system version (major word)
+template<typename PEClassType>
+uint16_t pe_properties_generic<PEClassType>::get_major_os_version() const
+{
+ return nt_headers_.OptionalHeader.MajorOperatingSystemVersion;
+}
+
+//Sets required subsystem version
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_subsystem_version(uint16_t major, uint16_t minor)
+{
+ nt_headers_.OptionalHeader.MinorSubsystemVersion = minor;
+ nt_headers_.OptionalHeader.MajorSubsystemVersion = major;
+}
+
+//Returns required subsystem version (minor word)
+template<typename PEClassType>
+uint16_t pe_properties_generic<PEClassType>::get_minor_subsystem_version() const
+{
+ return nt_headers_.OptionalHeader.MinorSubsystemVersion;
+}
+
+//Returns required subsystem version (major word)
+template<typename PEClassType>
+uint16_t pe_properties_generic<PEClassType>::get_major_subsystem_version() const
+{
+ return nt_headers_.OptionalHeader.MajorSubsystemVersion;
+}
+
+//Virtual Address (VA) to Relative Virtual Address (RVA) convertions for PE32
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::va_to_rva(uint32_t va, bool bound_check) const
+{
+ if(bound_check && static_cast<uint64_t>(va) - nt_headers_.OptionalHeader.ImageBase > pe_utils::max_dword)
+ throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion);
+
+ return static_cast<uint32_t>(va - nt_headers_.OptionalHeader.ImageBase);
+}
+
+//Virtual Address (VA) to Relative Virtual Address (RVA) convertions for PE32/PE64
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::va_to_rva(uint64_t va, bool bound_check) const
+{
+ if(bound_check && va - nt_headers_.OptionalHeader.ImageBase > pe_utils::max_dword)
+ throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion);
+
+ return static_cast<uint32_t>(va - nt_headers_.OptionalHeader.ImageBase);
+}
+
+//Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::rva_to_va_32(uint32_t rva) const
+{
+ if(!pe_utils::is_sum_safe(rva, static_cast<uint32_t>(nt_headers_.OptionalHeader.ImageBase)))
+ throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion);
+
+ return static_cast<uint32_t>(rva + nt_headers_.OptionalHeader.ImageBase);
+}
+
+//Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32/PE64
+template<typename PEClassType>
+uint64_t pe_properties_generic<PEClassType>::rva_to_va_64(uint32_t rva) const
+{
+ return static_cast<uint64_t>(rva) + nt_headers_.OptionalHeader.ImageBase;
+}
+
+//Returns number of sections
+template<typename PEClassType>
+uint16_t pe_properties_generic<PEClassType>::get_number_of_sections() const
+{
+ return nt_headers_.FileHeader.NumberOfSections;
+}
+
+//Sets number of sections
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_number_of_sections(uint16_t number)
+{
+ nt_headers_.FileHeader.NumberOfSections = number;
+}
+
+//Sets virtual size of image
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_size_of_image(uint32_t size)
+{
+ nt_headers_.OptionalHeader.SizeOfImage = size;
+}
+
+//Sets size of headers
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_size_of_headers(uint32_t size)
+{
+ nt_headers_.OptionalHeader.SizeOfHeaders = size;
+}
+
+//Sets size of optional headers
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_size_of_optional_header(uint16_t size)
+{
+ nt_headers_.FileHeader.SizeOfOptionalHeader = size;
+}
+
+//Returns nt headers data pointer
+template<typename PEClassType>
+char* pe_properties_generic<PEClassType>::get_nt_headers_ptr()
+{
+ return reinterpret_cast<char*>(&nt_headers_);
+}
+
+//Returns nt headers data pointer
+template<typename PEClassType>
+const char* pe_properties_generic<PEClassType>::get_nt_headers_ptr() const
+{
+ return reinterpret_cast<const char*>(&nt_headers_);
+}
+
+//Returns size of NT header
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_sizeof_nt_header() const
+{
+ return sizeof(typename PEClassType::NtHeaders);
+}
+
+//Returns size of optional headers
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_sizeof_opt_headers() const
+{
+ return sizeof(typename PEClassType::OptHeaders);
+}
+
+//Sets file alignment (no checks)
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_file_alignment_unchecked(uint32_t alignment)
+{
+ nt_headers_.OptionalHeader.FileAlignment = alignment;
+}
+
+//Sets base of code
+template<typename PEClassType>
+void pe_properties_generic<PEClassType>::set_base_of_code(uint32_t base)
+{
+ nt_headers_.OptionalHeader.BaseOfCode = base;
+}
+
+//Returns base of code
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_base_of_code() const
+{
+ return nt_headers_.OptionalHeader.BaseOfCode;
+}
+
+//Returns needed PE magic for PE or PE+ (from template parameters)
+template<typename PEClassType>
+uint32_t pe_properties_generic<PEClassType>::get_needed_magic() const
+{
+ return PEClassType::Id;
+}
+
+//Returns PE type of this image
+template<typename PEClassType>
+pe_type pe_properties_generic<PEClassType>::get_pe_type() const
+{
+ return PEClassType::Id == image_nt_optional_hdr32_magic ? pe_type_32 : pe_type_64;
+}
+
+//Two used instantiations for PE32 (PE) and PE64 (PE+)
+template class pe_properties_generic<pe_types_class_32>;
+template class pe_properties_generic<pe_types_class_64>;
+}
diff --git a/tools/pe_bliss/pe_properties_generic.h b/tools/pe_bliss/pe_properties_generic.h
new file mode 100644
index 0000000000..4ff906803c
--- /dev/null
+++ b/tools/pe_bliss/pe_properties_generic.h
@@ -0,0 +1,277 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "pe_properties.h"
+
+namespace pe_bliss
+{
+//Helper class to reduce code size and ease its editing
+template<
+ typename NtHeadersType,
+ typename OptHeadersType,
+ uint16_t IdVal,
+ typename BaseSizeType,
+ BaseSizeType ImportSnapFlagVal,
+ typename TLSStructType,
+ typename ConfigStructType>
+class pe_types
+{
+public:
+ typedef NtHeadersType NtHeaders; //NT HEADERS type
+ typedef OptHeadersType OptHeaders; //NT OPTIONAL HEADER type
+ typedef BaseSizeType BaseSize; //Base size of different values: DWORD or ULONGLONG
+ typedef TLSStructType TLSStruct; //TLS structure type
+ typedef ConfigStructType ConfigStruct; //Configuration structure type
+
+ static const uint16_t Id = IdVal; //Magic of PE or PE+
+ static const BaseSize ImportSnapFlag = ImportSnapFlagVal; //Import snap flag value
+};
+
+//Portable Executable derived class for PE and PE+
+//Describes PE/PE+ dependent things
+template<typename PEClassType>
+class pe_properties_generic : public pe_properties
+{
+public: //Constructor
+ virtual std::auto_ptr<pe_properties> duplicate() const;
+
+ //Fills properly PE structures
+ virtual void create_pe(uint32_t section_alignment, uint16_t subsystem);
+
+public:
+ //Destructor
+ virtual ~pe_properties_generic();
+
+
+public: //DIRECTORIES
+ //Returns true if directory exists
+ virtual bool directory_exists(uint32_t id) const;
+
+ //Removes directory
+ virtual void remove_directory(uint32_t id);
+
+ //Returns directory RVA
+ virtual uint32_t get_directory_rva(uint32_t id) const;
+ //Returns directory size
+ virtual uint32_t get_directory_size(uint32_t id) const;
+
+ //Sets directory RVA (just a value of PE header, no moving occurs)
+ virtual void set_directory_rva(uint32_t id, uint32_t rva);
+ //Sets directory size (just a value of PE header, no moving occurs)
+ virtual void set_directory_size(uint32_t id, uint32_t size);
+
+ //Strips only zero DATA_DIRECTORY entries to count = min_count
+ //Returns resulting number of data directories
+ //strip_iat_directory - if true, even not empty IAT directory will be stripped
+ virtual uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true);
+
+
+public: //IMAGE
+ //Returns PE type of this image
+ virtual pe_type get_pe_type() const;
+
+
+public: //PE HEADER
+ //Returns image base for PE32 and PE64 respectively
+ virtual uint32_t get_image_base_32() const;
+ virtual uint64_t get_image_base_64() const;
+
+ //Sets new image base for PE32
+ virtual void set_image_base(uint32_t base);
+ //Sets new image base for PE32/PE+
+ virtual void set_image_base_64(uint64_t base);
+
+ //Returns image entry point
+ virtual uint32_t get_ep() const;
+ //Sets image entry point
+ virtual void set_ep(uint32_t new_ep);
+
+ //Returns file alignment
+ virtual uint32_t get_file_alignment() const;
+ //Returns section alignment
+ virtual uint32_t get_section_alignment() const;
+
+ //Sets heap size commit for PE32 and PE64 respectively
+ virtual void set_heap_size_commit(uint32_t size);
+ virtual void set_heap_size_commit(uint64_t size);
+ //Sets heap size reserve for PE32 and PE64 respectively
+ virtual void set_heap_size_reserve(uint32_t size);
+ virtual void set_heap_size_reserve(uint64_t size);
+ //Sets stack size commit for PE32 and PE64 respectively
+ virtual void set_stack_size_commit(uint32_t size);
+ virtual void set_stack_size_commit(uint64_t size);
+ //Sets stack size reserve for PE32 and PE64 respectively
+ virtual void set_stack_size_reserve(uint32_t size);
+ virtual void set_stack_size_reserve(uint64_t size);
+
+ //Returns heap size commit for PE32 and PE64 respectively
+ virtual uint32_t get_heap_size_commit_32() const;
+ virtual uint64_t get_heap_size_commit_64() const;
+ //Returns heap size reserve for PE32 and PE64 respectively
+ virtual uint32_t get_heap_size_reserve_32() const;
+ virtual uint64_t get_heap_size_reserve_64() const;
+ //Returns stack size commit for PE32 and PE64 respectively
+ virtual uint32_t get_stack_size_commit_32() const;
+ virtual uint64_t get_stack_size_commit_64() const;
+ //Returns stack size reserve for PE32 and PE64 respectively
+ virtual uint32_t get_stack_size_reserve_32() const;
+ virtual uint64_t get_stack_size_reserve_64() const;
+
+ //Returns virtual size of image
+ virtual uint32_t get_size_of_image() const;
+
+ //Returns number of RVA and sizes (number of DATA_DIRECTORY entries)
+ virtual uint32_t get_number_of_rvas_and_sizes() const;
+ //Sets number of RVA and sizes (number of DATA_DIRECTORY entries)
+ virtual void set_number_of_rvas_and_sizes(uint32_t number);
+
+ //Returns PE characteristics
+ virtual uint16_t get_characteristics() const;
+ //Sets PE characteristics
+ virtual void set_characteristics(uint16_t ch);
+
+ //Returns size of headers
+ virtual uint32_t get_size_of_headers() const;
+
+ //Returns subsystem
+ virtual uint16_t get_subsystem() const;
+
+ //Sets subsystem
+ virtual void set_subsystem(uint16_t subsystem);
+
+ //Returns size of optional header
+ virtual uint16_t get_size_of_optional_header() const;
+
+ //Returns PE signature
+ virtual uint32_t get_pe_signature() const;
+
+ //Returns PE magic value
+ virtual uint32_t get_magic() const;
+
+ //Returns checksum of PE file from header
+ virtual uint32_t get_checksum() const;
+
+ //Sets checksum of PE file
+ virtual void set_checksum(uint32_t checksum);
+
+ //Returns timestamp of PE file from header
+ virtual uint32_t get_time_date_stamp() const;
+
+ //Sets timestamp of PE file
+ virtual void set_time_date_stamp(uint32_t timestamp);
+
+ //Returns Machine field value of PE file from header
+ virtual uint16_t get_machine() const;
+
+ //Sets Machine field value of PE file
+ virtual void set_machine(uint16_t machine);
+
+ //Returns DLL Characteristics
+ virtual uint16_t get_dll_characteristics() const;
+
+ //Sets DLL Characteristics
+ virtual void set_dll_characteristics(uint16_t characteristics);
+
+ //Sets required operation system version
+ virtual void set_os_version(uint16_t major, uint16_t minor);
+
+ //Returns required operation system version (minor word)
+ virtual uint16_t get_minor_os_version() const;
+
+ //Returns required operation system version (major word)
+ virtual uint16_t get_major_os_version() const;
+
+ //Sets required subsystem version
+ virtual void set_subsystem_version(uint16_t major, uint16_t minor);
+
+ //Returns required subsystem version (minor word)
+ virtual uint16_t get_minor_subsystem_version() const;
+
+ //Returns required subsystem version (major word)
+ virtual uint16_t get_major_subsystem_version() const;
+
+public: //ADDRESS CONVERTIONS
+ //Virtual Address (VA) to Relative Virtual Address (RVA) convertions
+ //for PE32 and PE64 respectively
+ //bound_check checks integer overflow
+ virtual uint32_t va_to_rva(uint32_t va, bool bound_check = true) const;
+ virtual uint32_t va_to_rva(uint64_t va, bool bound_check = true) const;
+
+ //Relative Virtual Address (RVA) to Virtual Address (VA) convertions
+ //for PE32 and PE64 respectively
+ virtual uint32_t rva_to_va_32(uint32_t rva) const;
+ virtual uint64_t rva_to_va_64(uint32_t rva) const;
+
+
+public: //SECTIONS
+ //Returns number of sections
+ virtual uint16_t get_number_of_sections() const;
+
+protected:
+ typename PEClassType::NtHeaders nt_headers_; //NT headers (PE32 or PE64)
+
+public:
+ //Sets number of sections
+ virtual void set_number_of_sections(uint16_t number);
+ //Sets virtual size of image
+ virtual void set_size_of_image(uint32_t size);
+ //Sets size of headers
+ virtual void set_size_of_headers(uint32_t size);
+ //Sets size of optional headers
+ virtual void set_size_of_optional_header(uint16_t size);
+ //Returns nt headers data pointer
+ virtual char* get_nt_headers_ptr();
+ //Returns nt headers data pointer
+ virtual const char* get_nt_headers_ptr() const;
+ //Returns size of NT header
+ virtual uint32_t get_sizeof_nt_header() const;
+ //Returns size of optional headers
+ virtual uint32_t get_sizeof_opt_headers() const;
+ //Sets file alignment (no checks)
+ virtual void set_file_alignment_unchecked(uint32_t alignment);
+ //Sets base of code
+ virtual void set_base_of_code(uint32_t base);
+ //Returns base of code
+ virtual uint32_t get_base_of_code() const;
+ //Returns needed PE magic for PE or PE+ (from template parameters)
+ virtual uint32_t get_needed_magic() const;
+};
+
+//Two used typedefs for PE32 (PE) and PE64 (PE+)
+typedef pe_types<pe_win::image_nt_headers32,
+ pe_win::image_optional_header32,
+ pe_win::image_nt_optional_hdr32_magic,
+ uint32_t,
+ pe_win::image_ordinal_flag32,
+ pe_win::image_tls_directory32,
+ pe_win::image_load_config_directory32> pe_types_class_32;
+
+typedef pe_types<pe_win::image_nt_headers64,
+ pe_win::image_optional_header64,
+ pe_win::image_nt_optional_hdr64_magic,
+ uint64_t,
+ pe_win::image_ordinal_flag64,
+ pe_win::image_tls_directory64,
+ pe_win::image_load_config_directory64> pe_types_class_64;
+
+typedef pe_properties_generic<pe_types_class_32> pe_properties_32;
+typedef pe_properties_generic<pe_types_class_64> pe_properties_64;
+}
diff --git a/tools/pe_bliss/pe_rebuilder.cpp b/tools/pe_bliss/pe_rebuilder.cpp
new file mode 100644
index 0000000000..faf5803b8c
--- /dev/null
+++ b/tools/pe_bliss/pe_rebuilder.cpp
@@ -0,0 +1,214 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "pe_rebuilder.h"
+#include "pe_base.h"
+#include "pe_structures.h"
+#include "pe_exception.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//Rebuilds PE image headers
+//If strip_dos_header is true, DOS headers partially will be used for PE headers
+//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically
+//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers)
+void rebuild_pe(pe_base& pe, image_dos_header& dos_header, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import)
+{
+ dos_header = pe.get_dos_header();
+
+ if(strip_dos_header)
+ {
+ //Strip stub overlay
+ pe.strip_stub_overlay();
+ //BaseOfCode NT Headers field now overlaps
+ //e_lfanew field, so we're acrually setting
+ //e_lfanew with this call
+ pe.set_base_of_code(8 * sizeof(uint16_t));
+ }
+ else
+ {
+ //Set start of PE headers
+ dos_header.e_lfanew = sizeof(image_dos_header)
+ + pe_utils::align_up(static_cast<uint32_t>(pe.get_stub_overlay().size()), sizeof(uint32_t));
+ }
+
+ section_list& sections = pe.get_image_sections();
+
+ //Calculate pointer to section data
+ size_t ptr_to_section_data = (strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)) + pe.get_sizeof_nt_header()
+ + pe_utils::align_up(pe.get_stub_overlay().size(), sizeof(uint32_t))
+ - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes())
+ + sections.size() * sizeof(image_section_header);
+
+ if(save_bound_import && pe.has_bound_import())
+ {
+ //It will be aligned to DWORD, because we're aligning to DWORD everything above it
+ pe.set_directory_rva(image_directory_entry_bound_import, static_cast<uint32_t>(ptr_to_section_data));
+ ptr_to_section_data += pe.get_directory_size(image_directory_entry_bound_import);
+ }
+
+ ptr_to_section_data = pe_utils::align_up(ptr_to_section_data, pe.get_file_alignment());
+
+ //Set size of headers and size of optional header
+ if(change_size_of_headers)
+ {
+ if(!pe.get_image_sections().empty())
+ {
+ if(static_cast<uint32_t>(ptr_to_section_data) > (*sections.begin()).get_virtual_address())
+ throw pe_exception("Headers of PE file are too long. Try to strip STUB or don't build bound import", pe_exception::cannot_rebuild_image);
+ }
+
+ pe.set_size_of_headers(static_cast<uint32_t>(ptr_to_section_data));
+ }
+
+ //Set number of sections in PE header
+ pe.update_number_of_sections();
+
+ pe.update_image_size();
+
+ pe.set_size_of_optional_header(static_cast<uint16_t>(pe.get_sizeof_opt_headers()
+ - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes())));
+
+ //Recalculate pointer to raw data according to section list
+ for(section_list::iterator it = sections.begin(); it != sections.end(); ++it)
+ {
+ //Save section headers PointerToRawData
+ (*it).set_pointer_to_raw_data(static_cast<uint32_t>(ptr_to_section_data));
+ ptr_to_section_data += (*it).get_aligned_raw_size(pe.get_file_alignment());
+ }
+}
+
+//Rebuild PE image and write it to "out" ostream
+//If strip_dos_header is true, DOS headers partially will be used for PE headers
+//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically
+//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers)
+void rebuild_pe(pe_base& pe, std::ostream& out, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import)
+{
+ if(out.bad())
+ throw pe_exception("Stream is bad", pe_exception::stream_is_bad);
+
+ if(save_bound_import && pe.has_bound_import())
+ {
+ if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true)
+ < pe.get_directory_size(image_directory_entry_bound_import))
+ throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory);
+ }
+
+ //Change ostream state
+ out.exceptions(std::ios::goodbit);
+ out.clear();
+
+ uint32_t original_bound_import_rva = pe.has_bound_import() ? pe.get_directory_rva(image_directory_entry_bound_import) : 0;
+ if(original_bound_import_rva && original_bound_import_rva > pe.get_size_of_headers())
+ {
+ //No need to do anything with bound import directory
+ //if it is placed inside of any section, not headers
+ original_bound_import_rva = 0;
+ save_bound_import = false;
+ }
+
+ {
+ image_dos_header dos_header;
+
+ //Rebuild PE image headers
+ rebuild_pe(pe, dos_header, strip_dos_header, change_size_of_headers, save_bound_import);
+
+ //Write DOS header
+ out.write(reinterpret_cast<const char*>(&dos_header), strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header));
+ }
+
+ //If we have stub overlay, write it too
+ {
+ const std::string& stub = pe.get_stub_overlay();
+ if(stub.size())
+ {
+ out.write(stub.data(), stub.size());
+ size_t aligned_size = pe_utils::align_up(stub.size(), sizeof(uint32_t));
+ //Align PE header, which is right after rich overlay
+ while(aligned_size > stub.size())
+ {
+ out.put('\0');
+ --aligned_size;
+ }
+ }
+ }
+
+ //Write NT headers
+ out.write(static_cast<const pe_base&>(pe).get_nt_headers_ptr(), pe.get_sizeof_nt_header()
+ - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes()));
+
+ //Write section headers
+ const section_list& sections = pe.get_image_sections();
+ for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it)
+ {
+ if(it == sections.end() - 1) //If last section encountered
+ {
+ image_section_header header((*it).get_raw_header());
+ header.SizeOfRawData = static_cast<uint32_t>((*it).get_raw_data().length()); //Set non-aligned actual data length for it
+ out.write(reinterpret_cast<const char*>(&header), sizeof(image_section_header));
+ }
+ else
+ {
+ out.write(reinterpret_cast<const char*>(&(*it).get_raw_header()), sizeof(image_section_header));
+ }
+ }
+
+ //Write bound import data if requested
+ if(save_bound_import && pe.has_bound_import())
+ {
+ out.write(pe.section_data_from_rva(original_bound_import_rva, section_data_raw, true),
+ pe.get_directory_size(image_directory_entry_bound_import));
+ }
+
+ //Write section data finally
+ for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it)
+ {
+ const section& s = *it;
+
+ std::streamoff wpos = out.tellp();
+
+ //Fill unused overlay data between sections with null bytes
+ for(unsigned int i = 0; i < s.get_pointer_to_raw_data() - wpos; i++)
+ out.put(0);
+
+ //Write raw section data
+ out.write(s.get_raw_data().data(), s.get_raw_data().length());
+ }
+}
+
+//Rebuild PE image and write it to "out" file
+//If strip_dos_header is true, DOS headers partially will be used for PE headers
+//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically
+//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers)
+void rebuild_pe(pe_base& pe, const char* out, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import)
+{
+ std::ofstream pe_file(out, std::ios::out | std::ios::binary | std::ios::trunc);
+ if(!pe_file)
+ {
+ throw pe_exception("Error in open file.", pe_exception::stream_is_bad);
+ }
+ rebuild_pe(pe, pe_file, strip_dos_header, change_size_of_headers, save_bound_import);
+}
+
+
+}
diff --git a/tools/pe_bliss/pe_rebuilder.h b/tools/pe_bliss/pe_rebuilder.h
new file mode 100644
index 0000000000..319807e5c9
--- /dev/null
+++ b/tools/pe_bliss/pe_rebuilder.h
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <ostream>
+#include <fstream>
+
+namespace pe_bliss
+{
+class pe_base;
+//Rebuilds PE image, writes resulting image to ostream "out". If strip_dos_header == true, DOS header will be stripped a little
+//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically
+//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers)
+void rebuild_pe(pe_base& pe, std::ostream& out, bool strip_dos_header = false, bool change_size_of_headers = true, bool save_bound_import = true);
+
+//Rebuild PE image and write it to "out" file
+//If strip_dos_header is true, DOS headers partially will be used for PE headers
+//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically
+//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers)
+void rebuild_pe(pe_base& pe, const char* out, bool strip_dos_header = false, bool change_size_of_headers = true, bool save_bound_import = true);
+
+}
diff --git a/tools/pe_bliss/pe_relocations.cpp b/tools/pe_bliss/pe_relocations.cpp
new file mode 100644
index 0000000000..d5357dd219
--- /dev/null
+++ b/tools/pe_bliss/pe_relocations.cpp
@@ -0,0 +1,320 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <string.h>
+#include "pe_relocations.h"
+#include "pe_properties_generic.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//RELOCATIONS
+//Default constructor
+relocation_entry::relocation_entry()
+ :rva_(0), type_(0)
+{}
+
+//Constructor from relocation item (WORD)
+relocation_entry::relocation_entry(uint16_t relocation_value)
+ :rva_(relocation_value & ((1 << 12) - 1)), type_(relocation_value >> 12)
+{}
+
+//Constructor from relative rva and relocation type
+relocation_entry::relocation_entry(uint16_t rrva, uint16_t type)
+ :rva_(rrva), type_(type)
+{}
+
+//Returns RVA of relocation
+uint16_t relocation_entry::get_rva() const
+{
+ return rva_;
+}
+
+//Returns type of relocation
+uint16_t relocation_entry::get_type() const
+{
+ return type_;
+}
+
+//Sets RVA of relocation
+void relocation_entry::set_rva(uint16_t rva)
+{
+ rva_ = rva;
+}
+
+//Sets type of relocation
+void relocation_entry::set_type(uint16_t type)
+{
+ type_ = type;
+}
+
+//Returns relocation item (rrva + type)
+uint16_t relocation_entry::get_item() const
+{
+ return rva_ | (type_ << 12);
+}
+
+//Sets relocation item (rrva + type)
+void relocation_entry::set_item(uint16_t item)
+{
+ rva_ = item & ((1 << 12) - 1);
+ type_ = item >> 12;
+}
+
+//Returns relocation list
+const relocation_table::relocation_list& relocation_table::get_relocations() const
+{
+ return relocations_;
+}
+
+//Adds relocation to table
+void relocation_table::add_relocation(const relocation_entry& entry)
+{
+ relocations_.push_back(entry);
+}
+
+//Default constructor
+relocation_table::relocation_table()
+ :rva_(0)
+{}
+
+//Constructor from RVA of relocation table
+relocation_table::relocation_table(uint32_t rva)
+ :rva_(rva)
+{}
+
+//Returns RVA of block
+uint32_t relocation_table::get_rva() const
+{
+ return rva_;
+}
+
+//Sets RVA of block
+void relocation_table::set_rva(uint32_t rva)
+{
+ rva_ = rva;
+}
+
+//Returns changeable relocation list
+relocation_table::relocation_list& relocation_table::get_relocations()
+{
+ return relocations_;
+}
+
+//Get relocation list of pe file, supports one-word sized relocations only
+//If list_absolute_entries = true, IMAGE_REL_BASED_ABSOLUTE will be listed
+const relocation_table_list get_relocations(const pe_base& pe, bool list_absolute_entries)
+{
+ relocation_table_list ret;
+
+ //If image does not have relocations
+ if(!pe.has_reloc())
+ return ret;
+
+ //Check the length in bytes of the section containing relocation directory
+ if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_basereloc),
+ pe.get_directory_rva(image_directory_entry_basereloc), section_data_virtual, true)
+ < sizeof(image_base_relocation))
+ throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory);
+
+ unsigned long current_pos = pe.get_directory_rva(image_directory_entry_basereloc);
+ //First IMAGE_BASE_RELOCATION table
+ image_base_relocation reloc_table = pe.section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true);
+
+ if(reloc_table.SizeOfBlock % 2)
+ throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory);
+
+ unsigned long reloc_size = pe.get_directory_size(image_directory_entry_basereloc);
+ unsigned long read_size = 0;
+
+ //reloc_table.VirtualAddress is not checked (not so important)
+ while(reloc_table.SizeOfBlock && read_size < reloc_size)
+ {
+ //Create relocation table
+ relocation_table table;
+ //Save RVA
+ table.set_rva(reloc_table.VirtualAddress);
+
+ if(!pe_utils::is_sum_safe(current_pos, reloc_table.SizeOfBlock))
+ throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory);
+
+ //List all relocations
+ for(unsigned long i = sizeof(image_base_relocation); i < reloc_table.SizeOfBlock; i += sizeof(uint16_t))
+ {
+ relocation_entry entry(pe.section_data_from_rva<uint16_t>(current_pos + i, section_data_virtual, true));
+ if(list_absolute_entries || entry.get_type() != image_rel_based_absolute)
+ table.add_relocation(entry);
+ }
+
+ //Save table
+ ret.push_back(table);
+
+ //Go to next relocation block
+ if(!pe_utils::is_sum_safe(current_pos, reloc_table.SizeOfBlock))
+ throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory);
+
+ current_pos += reloc_table.SizeOfBlock;
+ read_size += reloc_table.SizeOfBlock;
+ reloc_table = pe.section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true);
+ }
+
+ return ret;
+}
+
+//Simple relocations rebuilder
+//To keep PE file working, don't remove any of existing relocations in
+//relocation_table_list returned by a call to get_relocations() function
+//auto_strip_last_section - if true and relocations are placed in the last section, it will be automatically stripped
+//offset_from_section_start - offset from the beginning of reloc_section, where relocations data will be situated
+//If save_to_pe_header is true, PE header will be modified automatically
+const image_directory rebuild_relocations(pe_base& pe, const relocation_table_list& relocs, section& reloc_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section)
+{
+ //Check that reloc_section is attached to this PE image
+ if(!pe.section_attached(reloc_section))
+ throw pe_exception("Relocations section must be attached to PE file", pe_exception::section_is_not_attached);
+
+ uint32_t current_reloc_data_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t));
+
+ uint32_t needed_size = current_reloc_data_pos - offset_from_section_start; //Calculate needed size for relocation tables
+ uint32_t size_delta = needed_size;
+
+ uint32_t start_reloc_pos = current_reloc_data_pos;
+
+ //Enumerate relocation tables
+ for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it)
+ {
+ needed_size += static_cast<uint32_t>((*it).get_relocations().size() * sizeof(uint16_t) /* relocations */ + sizeof(image_base_relocation) /* table header */);
+ //End of each table will be DWORD-aligned
+ if((start_reloc_pos + needed_size - size_delta) % sizeof(uint32_t))
+ needed_size += sizeof(uint16_t); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation
+ }
+
+ //Check if reloc_section is last one. If it's not, check if there's enough place for relocations data
+ if(&reloc_section != &*(pe.get_image_sections().end() - 1) &&
+ (reloc_section.empty() || pe_utils::align_up(reloc_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + current_reloc_data_pos))
+ throw pe_exception("Insufficient space for relocations directory", pe_exception::insufficient_space);
+
+ std::string& raw_data = reloc_section.get_raw_data();
+
+ //This will be done only if reloc_section is the last section of image or for section with unaligned raw length of data
+ if(raw_data.length() < needed_size + current_reloc_data_pos)
+ raw_data.resize(needed_size + current_reloc_data_pos); //Expand section raw data
+
+ //Enumerate relocation tables
+ for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it)
+ {
+ //Create relocation table header
+ image_base_relocation reloc;
+ reloc.VirtualAddress = (*it).get_rva();
+ const relocation_table::relocation_list& reloc_list = (*it).get_relocations();
+ reloc.SizeOfBlock = static_cast<uint32_t>(sizeof(image_base_relocation) + sizeof(uint16_t) * reloc_list.size());
+ if((reloc_list.size() * sizeof(uint16_t)) % sizeof(uint32_t)) //If we must align end of relocation table
+ reloc.SizeOfBlock += sizeof(uint16_t);
+
+ memcpy(&raw_data[current_reloc_data_pos], &reloc, sizeof(reloc));
+ current_reloc_data_pos += sizeof(reloc);
+
+ //Enumerate relocations in table
+ for(relocation_table::relocation_list::const_iterator r = reloc_list.begin(); r != reloc_list.end(); ++r)
+ {
+ //Save relocations
+ uint16_t reloc_value = (*r).get_item();
+ memcpy(&raw_data[current_reloc_data_pos], &reloc_value, sizeof(reloc_value));
+ current_reloc_data_pos += sizeof(reloc_value);
+ }
+
+ if(current_reloc_data_pos % sizeof(uint32_t)) //If end of table is not DWORD-aligned
+ {
+ memset(&raw_data[current_reloc_data_pos], 0, sizeof(uint16_t)); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation
+ current_reloc_data_pos += sizeof(uint16_t);
+ }
+ }
+
+ image_directory ret(pe.rva_from_section_offset(reloc_section, start_reloc_pos), needed_size - size_delta);
+
+ //Adjust section raw and virtual sizes
+ pe.recalculate_section_sizes(reloc_section, auto_strip_last_section);
+
+ //If auto-rewrite of PE headers is required
+ if(save_to_pe_header)
+ {
+ pe.set_directory_rva(image_directory_entry_basereloc, ret.get_rva());
+ pe.set_directory_size(image_directory_entry_basereloc, ret.get_size());
+
+ pe.clear_characteristics_flags(image_file_relocs_stripped);
+ pe.set_dll_characteristics(pe.get_dll_characteristics() | image_dllcharacteristics_dynamic_base);
+ }
+
+ return ret;
+}
+
+//Recalculates image base with the help of relocation tables
+void rebase_image(pe_base& pe, const relocation_table_list& tables, uint64_t new_base)
+{
+ pe.get_pe_type() == pe_type_32
+ ? rebase_image_base<pe_types_class_32>(pe, tables, new_base)
+ : rebase_image_base<pe_types_class_64>(pe, tables, new_base);
+}
+
+//RELOCATIONS
+//Recalculates image base with the help of relocation tables
+//Recalculates VAs of DWORDS/QWORDS in image according to relocations
+//Notice: if you move some critical structures like TLS, image relocations will not fix new
+//positions of TLS VAs. Instead, some bytes that now doesn't belong to TLS will be fixed.
+//It is recommended to rebase image in the very beginning and move all structures afterwards.
+template<typename PEClassType>
+void rebase_image_base(pe_base& pe, const relocation_table_list& tables, uint64_t new_base)
+{
+ //Get current image base value
+ typename PEClassType::BaseSize image_base;
+ pe.get_image_base(image_base);
+
+ //ImageBase difference
+ typename PEClassType::BaseSize base_rel = static_cast<typename PEClassType::BaseSize>(static_cast<int64_t>(new_base) - image_base);
+
+ //We need to fix addresses from relocation tables
+ //Enumerate relocation tables
+ for(relocation_table_list::const_iterator it = tables.begin(); it != tables.end(); ++it)
+ {
+ const relocation_table::relocation_list& relocs = (*it).get_relocations();
+
+ uint32_t base_rva = (*it).get_rva();
+
+ //Enumerate relocations
+ for(relocation_table::relocation_list::const_iterator rel = relocs.begin(); rel != relocs.end(); ++rel)
+ {
+ //Skip ABSOLUTE entries
+ if((*rel).get_type() == pe_win::image_rel_based_absolute)
+ continue;
+
+ //Recalculate value by RVA and rewrite it
+ uint32_t current_rva = base_rva + (*rel).get_rva();
+ typename PEClassType::BaseSize value = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_rva, section_data_raw, true);
+ value += base_rel;
+ memcpy(pe.section_data_from_rva(current_rva, true), &value, sizeof(value));
+ }
+ }
+
+ //Finally, save new image base
+ pe.set_image_base_64(new_base);
+}
+}
diff --git a/tools/pe_bliss/pe_relocations.h b/tools/pe_bliss/pe_relocations.h
new file mode 100644
index 0000000000..1bc8b2a405
--- /dev/null
+++ b/tools/pe_bliss/pe_relocations.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <vector>
+#include "pe_structures.h"
+#include "pe_base.h"
+#include "pe_directory.h"
+
+namespace pe_bliss
+{
+//Class representing relocation entry
+//RVA of relocation is not actually RVA, but
+//(real RVA) - (RVA of table)
+class relocation_entry
+{
+public:
+ //Default constructor
+ relocation_entry();
+ //Constructor from relocation item (WORD)
+ explicit relocation_entry(uint16_t relocation_value);
+ //Constructor from relative rva and relocation type
+ relocation_entry(uint16_t rrva, uint16_t type);
+
+ //Returns RVA of relocation (actually, relative RVA from relocation table RVA)
+ uint16_t get_rva() const;
+ //Returns type of relocation
+ uint16_t get_type() const;
+
+ //Returns relocation item (rrva + type)
+ uint16_t get_item() const;
+
+public: //Setters do not change everything inside image, they are used by PE class
+ //You can also use them to rebuild relocations using rebuild_relocations()
+
+ //Sets RVA of relocation (actually, relative RVA from relocation table RVA)
+ void set_rva(uint16_t rva);
+ //Sets type of relocation
+ void set_type(uint16_t type);
+
+ //Sets relocation item (rrva + type)
+ void set_item(uint16_t item);
+
+private:
+ uint16_t rva_;
+ uint16_t type_;
+};
+
+//Class representing relocation table
+class relocation_table
+{
+public:
+ typedef std::vector<relocation_entry> relocation_list;
+
+public:
+ //Default constructor
+ relocation_table();
+ //Constructor from RVA of relocation table
+ explicit relocation_table(uint32_t rva);
+
+ //Returns relocation list
+ const relocation_list& get_relocations() const;
+ //Returns RVA of block
+ uint32_t get_rva() const;
+
+public: //These functions do not change everything inside image, they are used by PE class
+ //You can also use them to rebuild relocations using rebuild_relocations()
+
+ //Adds relocation to table
+ void add_relocation(const relocation_entry& entry);
+ //Returns changeable relocation list
+ relocation_list& get_relocations();
+ //Sets RVA of block
+ void set_rva(uint32_t rva);
+
+private:
+ uint32_t rva_;
+ relocation_list relocations_;
+};
+
+typedef std::vector<relocation_table> relocation_table_list;
+
+//Get relocation list of pe file, supports one-word sized relocations only
+//If list_absolute_entries = true, IMAGE_REL_BASED_ABSOLUTE will be listed
+const relocation_table_list get_relocations(const pe_base& pe, bool list_absolute_entries = false);
+
+//Simple relocations rebuilder
+//To keep PE file working, don't remove any of existing relocations in
+//relocation_table_list returned by a call to get_relocations() function
+//auto_strip_last_section - if true and relocations are placed in the last section, it will be automatically stripped
+//offset_from_section_start - offset from the beginning of reloc_section, where relocations data will be situated
+//If save_to_pe_header is true, PE header will be modified automatically
+const image_directory rebuild_relocations(pe_base& pe, const relocation_table_list& relocs, section& reloc_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true);
+
+//Recalculates image base with the help of relocation tables
+//Recalculates VAs of DWORDS/QWORDS in image according to relocations
+//Notice: if you move some critical structures like TLS, image relocations will not fix new
+//positions of TLS VAs. Instead, some bytes that now doesn't belong to TLS will be fixed.
+//It is recommended to rebase image in the very beginning and move all structures afterwards.
+void rebase_image(pe_base& pe, const relocation_table_list& tables, uint64_t new_base);
+
+template<typename PEClassType>
+void rebase_image_base(pe_base& pe, const relocation_table_list& tables, uint64_t new_base);
+}
diff --git a/tools/pe_bliss/pe_resource_manager.cpp b/tools/pe_bliss/pe_resource_manager.cpp
new file mode 100644
index 0000000000..0ee7840ff0
--- /dev/null
+++ b/tools/pe_bliss/pe_resource_manager.cpp
@@ -0,0 +1,286 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <algorithm>
+#include <sstream>
+#include <iomanip>
+#include <math.h>
+#include <string.h>
+#include "pe_resource_manager.h"
+#include "resource_internal.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//Constructor from root resource directory
+pe_resource_manager::pe_resource_manager(resource_directory& root_directory)
+ :pe_resource_viewer(root_directory), root_dir_edit_(root_directory)
+{}
+
+resource_directory& pe_resource_manager::get_root_directory()
+{
+ return root_dir_edit_;
+}
+
+//Removes all resources of given type or root name
+//If there's more than one directory entry of a given type, only the
+//first one will be deleted (that's an unusual situation)
+//Returns true if resource was deleted
+bool pe_resource_manager::remove_resource_type(resource_type type)
+{
+ //Search for resource type
+ resource_directory::entry_list& entries = root_dir_edit_.get_entry_list();
+ resource_directory::entry_list::iterator it = std::find_if(entries.begin(), entries.end(), resource_directory::id_entry_finder(type));
+ if(it != entries.end())
+ {
+ //Remove it, if found
+ entries.erase(it);
+ return true;
+ }
+
+ return false;
+}
+
+bool pe_resource_manager::remove_resource(const std::wstring& root_name)
+{
+ //Search for resource type
+ resource_directory::entry_list& entries = root_dir_edit_.get_entry_list();
+ resource_directory::entry_list::iterator it = std::find_if(entries.begin(), entries.end(), resource_directory::name_entry_finder(root_name));
+ if(it != entries.end())
+ {
+ //Remove it, if found
+ entries.erase(it);
+ return true;
+ }
+
+ return false;
+}
+
+//Helper to remove resource
+bool pe_resource_manager::remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder)
+{
+ //Search for resource type
+ resource_directory::entry_list& entries_type = root_dir_edit_.get_entry_list();
+ resource_directory::entry_list::iterator it_type = std::find_if(entries_type.begin(), entries_type.end(), root_finder);
+ if(it_type != entries_type.end())
+ {
+ //Search for resource name/ID with "finder"
+ resource_directory::entry_list& entries_name = (*it_type).get_resource_directory().get_entry_list();
+ resource_directory::entry_list::iterator it_name = std::find_if(entries_name.begin(), entries_name.end(), finder);
+ if(it_name != entries_name.end())
+ {
+ //Erase resource, if found
+ entries_name.erase(it_name);
+ if(entries_name.empty())
+ entries_type.erase(it_type);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//Removes all resource languages by resource type/root name and name
+//Deletes only one entry of given type and name
+//Returns true if resource was deleted
+bool pe_resource_manager::remove_resource(resource_type type, const std::wstring& name)
+{
+ return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(name));
+}
+
+bool pe_resource_manager::remove_resource(const std::wstring& root_name, const std::wstring& name)
+{
+ return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(name));
+}
+
+//Removes all resource languages by resource type/root name and ID
+//Deletes only one entry of given type and ID
+//Returns true if resource was deleted
+bool pe_resource_manager::remove_resource(resource_type type, uint32_t id)
+{
+ return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(id));
+}
+
+bool pe_resource_manager::remove_resource(const std::wstring& root_name, uint32_t id)
+{
+ return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(id));
+}
+
+//Helper to remove resource
+bool pe_resource_manager::remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder, uint32_t language)
+{
+ //Search for resource type
+ resource_directory::entry_list& entries_type = root_dir_edit_.get_entry_list();
+ resource_directory::entry_list::iterator it_type = std::find_if(entries_type.begin(), entries_type.end(), root_finder);
+ if(it_type != entries_type.end())
+ {
+ //Search for resource name/ID with "finder"
+ resource_directory::entry_list& entries_name = (*it_type).get_resource_directory().get_entry_list();
+ resource_directory::entry_list::iterator it_name = std::find_if(entries_name.begin(), entries_name.end(), finder);
+ if(it_name != entries_name.end())
+ {
+ //Search for resource language
+ resource_directory::entry_list& entries_lang = (*it_name).get_resource_directory().get_entry_list();
+ resource_directory::entry_list::iterator it_lang = std::find_if(entries_lang.begin(), entries_lang.end(), resource_directory::id_entry_finder(language));
+ if(it_lang != entries_lang.end())
+ {
+ //Erase resource, if found
+ entries_lang.erase(it_lang);
+ if(entries_lang.empty())
+ {
+ entries_name.erase(it_name);
+ if(entries_name.empty())
+ entries_type.erase(it_type);
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+//Removes resource language by resource type/root name and name
+//Deletes only one entry of given type, name and language
+//Returns true if resource was deleted
+bool pe_resource_manager::remove_resource(resource_type type, const std::wstring& name, uint32_t language)
+{
+ return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(name), language);
+}
+
+bool pe_resource_manager::remove_resource(const std::wstring& root_name, const std::wstring& name, uint32_t language)
+{
+ return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(name), language);
+}
+
+//Removes recource language by resource type/root name and ID
+//Deletes only one entry of given type, ID and language
+//Returns true if resource was deleted
+bool pe_resource_manager::remove_resource(resource_type type, uint32_t id, uint32_t language)
+{
+ return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(id), language);
+}
+
+bool pe_resource_manager::remove_resource(const std::wstring& root_name, uint32_t id, uint32_t language)
+{
+ return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(id), language);
+}
+
+//Helper to add/replace resource
+void pe_resource_manager::add_resource(const std::string& data, resource_type type, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp)
+{
+ resource_directory_entry new_type_entry;
+ new_type_entry.set_id(type);
+
+ add_resource(data, new_type_entry, resource_directory::entry_finder(type), new_entry, finder, language, codepage, timestamp);
+}
+
+//Helper to add/replace resource
+void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp)
+{
+ resource_directory_entry new_type_entry;
+ new_type_entry.set_name(root_name);
+
+ add_resource(data, new_type_entry, resource_directory::entry_finder(root_name), new_entry, finder, language, codepage, timestamp);
+}
+
+//Helper to add/replace resource
+void pe_resource_manager::add_resource(const std::string& data, resource_directory_entry& new_root_entry, const resource_directory::entry_finder& root_finder, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp)
+{
+ //Search for resource type
+ resource_directory::entry_list* entries = &root_dir_edit_.get_entry_list();
+ resource_directory::entry_list::iterator it = std::find_if(entries->begin(), entries->end(), root_finder);
+ if(it == entries->end())
+ {
+ //Add resource type directory, if it was not found
+ resource_directory dir;
+ dir.set_timestamp(timestamp);
+ new_root_entry.add_resource_directory(dir);
+ entries->push_back(new_root_entry);
+ it = entries->end() - 1;
+ }
+
+ //Search for resource name/ID directory with "finder"
+ entries = &(*it).get_resource_directory().get_entry_list();
+ it = std::find_if(entries->begin(), entries->end(), finder);
+ if(it == entries->end())
+ {
+ //Add resource name/ID directory, if it was not found
+ resource_directory dir;
+ dir.set_timestamp(timestamp);
+ new_entry.add_resource_directory(dir);
+ entries->push_back(new_entry);
+ it = entries->end() - 1;
+ }
+
+ //Search for data resource entry by language
+ entries = &(*it).get_resource_directory().get_entry_list();
+ it = std::find_if(entries->begin(), entries->end(), resource_directory::id_entry_finder(language));
+ if(it != entries->end())
+ entries->erase(it); //Erase it, if found
+
+ //Add new data entry
+ resource_directory_entry new_dir_data_entry;
+ resource_data_entry data_dir(data, codepage);
+ new_dir_data_entry.add_data_entry(data_dir);
+ new_dir_data_entry.set_id(language);
+ entries->push_back(new_dir_data_entry);
+}
+
+//Adds resource. If resource already exists, replaces it
+void pe_resource_manager::add_resource(const std::string& data, resource_type type, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp)
+{
+ resource_directory_entry new_entry;
+ new_entry.set_name(name);
+
+ add_resource(data, type, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp);
+}
+
+//Adds resource. If resource already exists, replaces it
+void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp)
+{
+ resource_directory_entry new_entry;
+ new_entry.set_name(name);
+
+ add_resource(data, root_name, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp);
+}
+
+//Adds resource. If resource already exists, replaces it
+void pe_resource_manager::add_resource(const std::string& data, resource_type type, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp)
+{
+ resource_directory_entry new_entry;
+ new_entry.set_id(id);
+
+ add_resource(data, type, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp);
+}
+
+//Adds resource. If resource already exists, replaces it
+void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp)
+{
+ resource_directory_entry new_entry;
+ new_entry.set_id(id);
+
+ add_resource(data, root_name, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp);
+}
+}
diff --git a/tools/pe_bliss/pe_resource_manager.h b/tools/pe_bliss/pe_resource_manager.h
new file mode 100644
index 0000000000..85d7f44a8a
--- /dev/null
+++ b/tools/pe_bliss/pe_resource_manager.h
@@ -0,0 +1,113 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <map>
+#include <sstream>
+#include <string>
+#include <memory>
+#include "pe_base.h"
+#include "pe_structures.h"
+#include "pe_resources.h"
+#include "message_table.h"
+#include "file_version_info.h"
+#include "pe_resource_viewer.h"
+#include "resource_data_info.h"
+
+namespace pe_bliss
+{
+//Derived class to edit PE resources
+class pe_resource_manager : public pe_resource_viewer
+{
+public:
+ //Constructor from root resource directory
+ explicit pe_resource_manager(resource_directory& root_directory);
+
+ resource_directory& get_root_directory();
+
+public: //Resource editing
+ //Removes all resources of given type or root name
+ //If there's more than one directory entry of a given type, only the
+ //first one will be deleted (that's an unusual situation)
+ //Returns true if resource was deleted
+ bool remove_resource_type(resource_type type);
+ bool remove_resource(const std::wstring& root_name);
+
+ //Removes all resource languages by resource type/root name and name
+ //Deletes only one entry of given type and name
+ //Returns true if resource was deleted
+ bool remove_resource(resource_type type, const std::wstring& name);
+ bool remove_resource(const std::wstring& root_name, const std::wstring& name);
+ //Removes all resource languages by resource type/root name and ID
+ //Deletes only one entry of given type and ID
+ //Returns true if resource was deleted
+ bool remove_resource(resource_type type, uint32_t id);
+ bool remove_resource(const std::wstring& root_name, uint32_t id);
+
+ //Removes resource language by resource type/root name and name
+ //Deletes only one entry of given type, name and language
+ //Returns true if resource was deleted
+ bool remove_resource(resource_type type, const std::wstring& name, uint32_t language);
+ bool remove_resource(const std::wstring& root_name, const std::wstring& name, uint32_t language);
+ //Removes recource language by resource type/root name and ID
+ //Deletes only one entry of given type, ID and language
+ //Returns true if resource was deleted
+ bool remove_resource(resource_type type, uint32_t id, uint32_t language);
+ bool remove_resource(const std::wstring& root_name, uint32_t id, uint32_t language);
+
+ //Adds resource. If resource already exists, replaces it
+ //timestamp will be used for directories that will be added
+ void add_resource(const std::string& data, resource_type type, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0);
+ void add_resource(const std::string& data, const std::wstring& root_name, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0);
+ //Adds resource. If resource already exists, replaces it
+ //timestamp will be used for directories that will be added
+ void add_resource(const std::string& data, resource_type type, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0);
+ void add_resource(const std::string& data, const std::wstring& root_name, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0);
+
+public:
+ //Helpers to add/replace resource
+ void add_resource(const std::string& data, resource_type type,
+ resource_directory_entry& new_entry,
+ const resource_directory::entry_finder& finder,
+ uint32_t language, uint32_t codepage, uint32_t timestamp);
+
+ void add_resource(const std::string& data, const std::wstring& root_name,
+ resource_directory_entry& new_entry,
+ const resource_directory::entry_finder& finder,
+ uint32_t language, uint32_t codepage, uint32_t timestamp);
+
+ void add_resource(const std::string& data, resource_directory_entry& new_root_entry,
+ const resource_directory::entry_finder& root_finder,
+ resource_directory_entry& new_entry,
+ const resource_directory::entry_finder& finder,
+ uint32_t language, uint32_t codepage, uint32_t timestamp);
+
+private:
+ //Root resource directory. We're not copying it, because it might be heavy
+ resource_directory& root_dir_edit_;
+
+ //Helper to remove resource
+ bool remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder);
+
+ //Helper to remove resource
+ bool remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder, uint32_t language);
+};
+}
diff --git a/tools/pe_bliss/pe_resource_viewer.cpp b/tools/pe_bliss/pe_resource_viewer.cpp
new file mode 100644
index 0000000000..712cc28d9b
--- /dev/null
+++ b/tools/pe_bliss/pe_resource_viewer.cpp
@@ -0,0 +1,382 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <algorithm>
+#include <cmath>
+#include "pe_resource_viewer.h"
+#include "pe_structures.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//Constructor from root resource_directory
+pe_resource_viewer::pe_resource_viewer(const resource_directory& root_directory)
+ :root_dir_(root_directory)
+{}
+
+const resource_directory& pe_resource_viewer::get_root_directory() const
+{
+ return root_dir_;
+}
+
+//Finder helpers
+bool pe_resource_viewer::has_name::operator()(const resource_directory_entry& entry) const
+{
+ return entry.is_named();
+}
+
+bool pe_resource_viewer::has_id::operator()(const resource_directory_entry& entry) const
+{
+ return !entry.is_named();
+}
+
+//Lists resource types existing in PE file (non-named only)
+const pe_resource_viewer::resource_type_list pe_resource_viewer::list_resource_types() const
+{
+ resource_type_list ret;
+
+ //Get root directory entries list
+ const resource_directory::entry_list& entries = root_dir_.get_entry_list();
+ for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it)
+ {
+ //List all non-named items
+ if(!(*it).is_named())
+ ret.push_back((*it).get_id());
+ }
+
+ return ret;
+}
+
+//Returns true if resource type exists
+bool pe_resource_viewer::resource_exists(resource_type type) const
+{
+ const resource_directory::entry_list& entries = root_dir_.get_entry_list();
+ return std::find_if(entries.begin(), entries.end(), resource_directory::id_entry_finder(type)) != entries.end();
+}
+
+//Returns true if resource name exists
+bool pe_resource_viewer::resource_exists(const std::wstring& root_name) const
+{
+ const resource_directory::entry_list& entries = root_dir_.get_entry_list();
+ return std::find_if(entries.begin(), entries.end(), resource_directory::name_entry_finder(root_name)) != entries.end();
+}
+
+//Helper function to get name list from entry list
+const pe_resource_viewer::resource_name_list pe_resource_viewer::get_name_list(const resource_directory::entry_list& entries)
+{
+ resource_name_list ret;
+
+ for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it)
+ {
+ //List all named items
+ if((*it).is_named())
+ ret.push_back((*it).get_name());
+ }
+
+ return ret;
+}
+
+//Helper function to get ID list from entry list
+const pe_resource_viewer::resource_id_list pe_resource_viewer::get_id_list(const resource_directory::entry_list& entries)
+{
+ resource_id_list ret;
+
+ for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it)
+ {
+ //List all non-named items
+ if(!(*it).is_named())
+ ret.push_back((*it).get_id());
+ }
+
+ return ret;
+}
+
+//Lists resource names existing in PE file by resource type
+const pe_resource_viewer::resource_name_list pe_resource_viewer::list_resource_names(resource_type type) const
+{
+ return get_name_list(root_dir_.entry_by_id(type).get_resource_directory().get_entry_list());
+}
+
+//Lists resource names existing in PE file by resource name
+const pe_resource_viewer::resource_name_list pe_resource_viewer::list_resource_names(const std::wstring& root_name) const
+{
+ return get_name_list(root_dir_.entry_by_name(root_name).get_resource_directory().get_entry_list());
+}
+
+//Lists resource IDs existing in PE file by resource type
+const pe_resource_viewer::resource_id_list pe_resource_viewer::list_resource_ids(resource_type type) const
+{
+ return get_id_list(root_dir_.entry_by_id(type).get_resource_directory().get_entry_list());
+}
+
+//Lists resource IDs existing in PE file by resource name
+const pe_resource_viewer::resource_id_list pe_resource_viewer::list_resource_ids(const std::wstring& root_name) const
+{
+ return get_id_list(root_dir_.entry_by_name(root_name).get_resource_directory().get_entry_list());
+}
+
+//Returns resource count by type
+unsigned long pe_resource_viewer::get_resource_count(resource_type type) const
+{
+ return static_cast<unsigned long>(
+ root_dir_ //Type directory
+ .entry_by_id(type)
+ .get_resource_directory() //Name/ID directory
+ .get_entry_list()
+ .size());
+}
+
+//Returns resource count by name
+unsigned long pe_resource_viewer::get_resource_count(const std::wstring& root_name) const
+{
+ return static_cast<unsigned long>(
+ root_dir_ //Type directory
+ .entry_by_name(root_name)
+ .get_resource_directory() //Name/ID directory
+ .get_entry_list()
+ .size());
+}
+
+//Returns language count of resource by resource type and name
+unsigned long pe_resource_viewer::get_language_count(resource_type type, const std::wstring& name) const
+{
+ const resource_directory::entry_list& entries =
+ root_dir_ //Type directory
+ .entry_by_id(type)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_name(name)
+ .get_resource_directory() //Language directory
+ .get_entry_list();
+
+ return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id()));
+}
+
+//Returns language count of resource by resource names
+unsigned long pe_resource_viewer::get_language_count(const std::wstring& root_name, const std::wstring& name) const
+{
+ const resource_directory::entry_list& entries =
+ root_dir_ //Type directory
+ .entry_by_name(root_name)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_name(name)
+ .get_resource_directory() //Language directory
+ .get_entry_list();
+
+ return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id()));
+}
+
+//Returns language count of resource by resource type and ID
+unsigned long pe_resource_viewer::get_language_count(resource_type type, uint32_t id) const
+{
+ const resource_directory::entry_list& entries =
+ root_dir_ //Type directory
+ .entry_by_id(type)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_id(id)
+ .get_resource_directory() //Language directory
+ .get_entry_list();
+
+ return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id()));
+}
+
+//Returns language count of resource by resource name and ID
+unsigned long pe_resource_viewer::get_language_count(const std::wstring& root_name, uint32_t id) const
+{
+ const resource_directory::entry_list& entries =
+ root_dir_ //Type directory
+ .entry_by_name(root_name)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_id(id)
+ .get_resource_directory() //Language directory
+ .get_entry_list();
+
+ return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id()));
+}
+
+//Lists resource languages by resource type and name
+const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(resource_type type, const std::wstring& name) const
+{
+ const resource_directory::entry_list& entries =
+ root_dir_ //Type directory
+ .entry_by_id(type)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_name(name)
+ .get_resource_directory() //Language directory
+ .get_entry_list();
+
+ return get_id_list(entries);
+}
+
+//Lists resource languages by resource names
+const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(const std::wstring& root_name, const std::wstring& name) const
+{
+ const resource_directory::entry_list& entries =
+ root_dir_ //Type directory
+ .entry_by_name(root_name)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_name(name)
+ .get_resource_directory() //Language directory
+ .get_entry_list();
+
+ return get_id_list(entries);
+}
+
+//Lists resource languages by resource type and ID
+const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(resource_type type, uint32_t id) const
+{
+ const resource_directory::entry_list& entries =
+ root_dir_ //Type directory
+ .entry_by_id(type)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_id(id)
+ .get_resource_directory() //Language directory
+ .get_entry_list();
+
+ return get_id_list(entries);
+}
+
+//Lists resource languages by resource name and ID
+const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(const std::wstring& root_name, uint32_t id) const
+{
+ const resource_directory::entry_list& entries =
+ root_dir_ //Type directory
+ .entry_by_name(root_name)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_id(id)
+ .get_resource_directory() //Language directory
+ .get_entry_list();
+
+ return get_id_list(entries);
+}
+
+//Returns raw resource data by type, name and language
+const resource_data_info pe_resource_viewer::get_resource_data_by_name(uint32_t language, resource_type type, const std::wstring& name) const
+{
+ return resource_data_info(root_dir_ //Type directory
+ .entry_by_id(type)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_name(name)
+ .get_resource_directory() //Language directory
+ .entry_by_id(language)
+ .get_data_entry()); //Data directory
+}
+
+//Returns raw resource data by root name, name and language
+const resource_data_info pe_resource_viewer::get_resource_data_by_name(uint32_t language, const std::wstring& root_name, const std::wstring& name) const
+{
+ return resource_data_info(root_dir_ //Type directory
+ .entry_by_name(root_name)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_name(name)
+ .get_resource_directory() //Language directory
+ .entry_by_id(language)
+ .get_data_entry()); //Data directory
+}
+
+//Returns raw resource data by type, ID and language
+const resource_data_info pe_resource_viewer::get_resource_data_by_id(uint32_t language, resource_type type, uint32_t id) const
+{
+ return resource_data_info(root_dir_ //Type directory
+ .entry_by_id(type)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_id(id)
+ .get_resource_directory() //Language directory
+ .entry_by_id(language)
+ .get_data_entry()); //Data directory
+}
+
+//Returns raw resource data by root name, ID and language
+const resource_data_info pe_resource_viewer::get_resource_data_by_id(uint32_t language, const std::wstring& root_name, uint32_t id) const
+{
+ return resource_data_info(root_dir_ //Type directory
+ .entry_by_name(root_name)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_id(id)
+ .get_resource_directory() //Language directory
+ .entry_by_id(language)
+ .get_data_entry()); //Data directory
+}
+
+//Returns raw resource data by type, name and index in language directory (instead of language)
+const resource_data_info pe_resource_viewer::get_resource_data_by_name(resource_type type, const std::wstring& name, uint32_t index) const
+{
+ const resource_directory::entry_list& entries = root_dir_ //Type directory
+ .entry_by_id(type)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_name(name)
+ .get_resource_directory() //Language directory
+ .get_entry_list();
+
+ if(entries.size() <= index)
+ throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found);
+
+ return resource_data_info(entries.at(index).get_data_entry()); //Data directory
+}
+
+//Returns raw resource data by root name, name and index in language directory (instead of language)
+const resource_data_info pe_resource_viewer::get_resource_data_by_name(const std::wstring& root_name, const std::wstring& name, uint32_t index) const
+{
+ const resource_directory::entry_list& entries = root_dir_ //Type directory
+ .entry_by_name(root_name)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_name(name)
+ .get_resource_directory() //Language directory
+ .get_entry_list();
+
+ if(entries.size() <= index)
+ throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found);
+
+ return resource_data_info(entries.at(index).get_data_entry()); //Data directory
+}
+
+//Returns raw resource data by type, ID and index in language directory (instead of language)
+const resource_data_info pe_resource_viewer::get_resource_data_by_id(resource_type type, uint32_t id, uint32_t index) const
+{
+ const resource_directory::entry_list& entries = root_dir_ //Type directory
+ .entry_by_id(type)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_id(id)
+ .get_resource_directory() //Language directory
+ .get_entry_list();
+
+ if(entries.size() <= index)
+ throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found);
+
+ return resource_data_info(entries.at(index).get_data_entry()); //Data directory
+}
+
+//Returns raw resource data by root name, ID and index in language directory (instead of language)
+const resource_data_info pe_resource_viewer::get_resource_data_by_id(const std::wstring& root_name, uint32_t id, uint32_t index) const
+{
+ const resource_directory::entry_list& entries = root_dir_ //Type directory
+ .entry_by_name(root_name)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_id(id)
+ .get_resource_directory() //Language directory
+ .get_entry_list();
+
+ if(entries.size() <= index)
+ throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found);
+
+ return resource_data_info(entries.at(index).get_data_entry()); //Data directory
+}
+}
diff --git a/tools/pe_bliss/pe_resource_viewer.h b/tools/pe_bliss/pe_resource_viewer.h
new file mode 100644
index 0000000000..e585da6a87
--- /dev/null
+++ b/tools/pe_bliss/pe_resource_viewer.h
@@ -0,0 +1,153 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <map>
+#include <string>
+#include "pe_structures.h"
+#include "pe_resources.h"
+#include "message_table.h"
+#include "resource_data_info.h"
+
+namespace pe_bliss
+{
+ //PE resource manager allows to read resources from PE files
+class pe_resource_viewer
+{
+public:
+ //Resource type enumeration
+ enum resource_type
+ {
+ resource_cursor = 1,
+ resource_bitmap = 2,
+ resource_icon = 3,
+ resource_menu = 4,
+ resource_dialog = 5,
+ resource_string = 6,
+ resource_fontdir = 7,
+ resource_font = 8,
+ resource_accelerator = 9,
+ resource_rcdata = 10,
+ resource_message_table = 11,
+ resource_cursor_group = 12,
+ resource_icon_group = 14,
+ resource_version = 16,
+ resource_dlginclude = 17,
+ resource_plugplay = 19,
+ resource_vxd = 20,
+ resource_anicursor = 21,
+ resource_aniicon = 22,
+ resource_html = 23,
+ resource_manifest = 24
+ };
+
+public:
+ //Some useful typedefs
+ typedef std::vector<uint32_t> resource_type_list;
+ typedef std::vector<uint32_t> resource_id_list;
+ typedef std::vector<std::wstring> resource_name_list;
+ typedef std::vector<uint32_t> resource_language_list;
+
+public:
+ //Constructor from root resource_directory from PE file
+ explicit pe_resource_viewer(const resource_directory& root_directory);
+
+ const resource_directory& get_root_directory() const;
+
+ //Lists resource types existing in PE file (non-named only)
+ const resource_type_list list_resource_types() const;
+ //Returns true if resource type exists
+ bool resource_exists(resource_type type) const;
+ //Returns true if resource name exists
+ bool resource_exists(const std::wstring& root_name) const;
+
+ //Lists resource names existing in PE file by resource type
+ const resource_name_list list_resource_names(resource_type type) const;
+ //Lists resource names existing in PE file by resource name
+ const resource_name_list list_resource_names(const std::wstring& root_name) const;
+ //Lists resource IDs existing in PE file by resource type
+ const resource_id_list list_resource_ids(resource_type type) const;
+ //Lists resource IDs existing in PE file by resource name
+ const resource_id_list list_resource_ids(const std::wstring& root_name) const;
+ //Returns resource count by type
+ unsigned long get_resource_count(resource_type type) const;
+ //Returns resource count by name
+ unsigned long get_resource_count(const std::wstring& root_name) const;
+
+ //Returns language count of resource by resource type and name
+ unsigned long get_language_count(resource_type type, const std::wstring& name) const;
+ //Returns language count of resource by resource names
+ unsigned long get_language_count(const std::wstring& root_name, const std::wstring& name) const;
+ //Returns language count of resource by resource type and ID
+ unsigned long get_language_count(resource_type type, uint32_t id) const;
+ //Returns language count of resource by resource name and ID
+ unsigned long get_language_count(const std::wstring& root_name, uint32_t id) const;
+ //Lists resource languages by resource type and name
+ const resource_language_list list_resource_languages(resource_type type, const std::wstring& name) const;
+ //Lists resource languages by resource names
+ const resource_language_list list_resource_languages(const std::wstring& root_name, const std::wstring& name) const;
+ //Lists resource languages by resource type and ID
+ const resource_language_list list_resource_languages(resource_type type, uint32_t id) const;
+ //Lists resource languages by resource name and ID
+ const resource_language_list list_resource_languages(const std::wstring& root_name, uint32_t id) const;
+
+ //Returns raw resource data by type, name and language
+ const resource_data_info get_resource_data_by_name(uint32_t language, resource_type type, const std::wstring& name) const;
+ //Returns raw resource data by root name, name and language
+ const resource_data_info get_resource_data_by_name(uint32_t language, const std::wstring& root_name, const std::wstring& name) const;
+ //Returns raw resource data by type, ID and language
+ const resource_data_info get_resource_data_by_id(uint32_t language, resource_type type, uint32_t id) const;
+ //Returns raw resource data by root name, ID and language
+ const resource_data_info get_resource_data_by_id(uint32_t language, const std::wstring& root_name, uint32_t id) const;
+ //Returns raw resource data by type, name and index in language directory (instead of language)
+ const resource_data_info get_resource_data_by_name(resource_type type, const std::wstring& name, uint32_t index = 0) const;
+ //Returns raw resource data by root name, name and index in language directory (instead of language)
+ const resource_data_info get_resource_data_by_name(const std::wstring& root_name, const std::wstring& name, uint32_t index = 0) const;
+ //Returns raw resource data by type, ID and index in language directory (instead of language)
+ const resource_data_info get_resource_data_by_id(resource_type type, uint32_t id, uint32_t index = 0) const;
+ //Returns raw resource data by root name, ID and index in language directory (instead of language)
+ const resource_data_info get_resource_data_by_id(const std::wstring& root_name, uint32_t id, uint32_t index = 0) const;
+
+protected:
+ //Root resource directory. We're not copying it, because it might be heavy
+ const resource_directory& root_dir_;
+
+ //Helper function to get ID list from entry list
+ static const resource_id_list get_id_list(const resource_directory::entry_list& entries);
+ //Helper function to get name list from entry list
+ static const resource_name_list get_name_list(const resource_directory::entry_list& entries);
+
+protected:
+ //Helper structure - finder of resource_directory_entry that is named
+ struct has_name
+ {
+ public:
+ bool operator()(const resource_directory_entry& entry) const;
+ };
+
+ //Helper structure - finder of resource_directory_entry that is not named (has id)
+ struct has_id
+ {
+ public:
+ bool operator()(const resource_directory_entry& entry) const;
+ };
+};
+}
diff --git a/tools/pe_bliss/pe_resources.cpp b/tools/pe_bliss/pe_resources.cpp
new file mode 100644
index 0000000000..189aba1f76
--- /dev/null
+++ b/tools/pe_bliss/pe_resources.cpp
@@ -0,0 +1,726 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <algorithm>
+#include <string.h>
+#include "pe_resources.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//RESOURCES
+//Default constructor
+resource_data_entry::resource_data_entry()
+ :codepage_(0)
+{}
+
+//Constructor from data
+resource_data_entry::resource_data_entry(const std::string& data, uint32_t codepage)
+ :codepage_(codepage), data_(data)
+{}
+
+//Returns resource data codepage
+uint32_t resource_data_entry::get_codepage() const
+{
+ return codepage_;
+}
+
+//Returns resource data
+const std::string& resource_data_entry::get_data() const
+{
+ return data_;
+}
+
+//Sets resource data codepage
+void resource_data_entry::set_codepage(uint32_t codepage)
+{
+ codepage_ = codepage;
+}
+
+//Sets resource data
+void resource_data_entry::set_data(const std::string& data)
+{
+ data_ = data;
+}
+
+//Default constructor
+resource_directory_entry::includes::includes()
+ :data_(0)
+{}
+
+//Default constructor
+resource_directory_entry::resource_directory_entry()
+ :id_(0), includes_data_(false), named_(false)
+{}
+
+//Copy constructor
+resource_directory_entry::resource_directory_entry(const resource_directory_entry& other)
+ :id_(other.id_), name_(other.name_), includes_data_(other.includes_data_), named_(other.named_)
+{
+ //If union'ed pointer is not zero
+ if(other.ptr_.data_)
+ {
+ if(other.includes_data())
+ ptr_.data_ = new resource_data_entry(*other.ptr_.data_);
+ else
+ ptr_.dir_ = new resource_directory(*other.ptr_.dir_);
+ }
+}
+
+//Copy assignment operator
+resource_directory_entry& resource_directory_entry::operator=(const resource_directory_entry& other)
+{
+ release();
+
+ id_ = other.id_;
+ name_ = other.name_;
+ includes_data_ = other.includes_data_;
+ named_ = other.named_;
+
+ //If other union'ed pointer is not zero
+ if(other.ptr_.data_)
+ {
+ if(other.includes_data())
+ ptr_.data_ = new resource_data_entry(*other.ptr_.data_);
+ else
+ ptr_.dir_ = new resource_directory(*other.ptr_.dir_);
+ }
+
+ return *this;
+}
+
+//Destroys included data
+void resource_directory_entry::release()
+{
+ //If union'ed pointer is not zero
+ if(ptr_.data_)
+ {
+ if(includes_data())
+ delete ptr_.data_;
+ else
+ delete ptr_.dir_;
+
+ ptr_.data_ = 0;
+ }
+}
+
+//Destructor
+resource_directory_entry::~resource_directory_entry()
+{
+ release();
+}
+
+//Returns entry ID
+uint32_t resource_directory_entry::get_id() const
+{
+ return id_;
+}
+
+//Returns entry name
+const std::wstring& resource_directory_entry::get_name() const
+{
+ return name_;
+}
+
+//Returns true, if entry has name
+//Returns false, if entry has ID
+bool resource_directory_entry::is_named() const
+{
+ return named_;
+}
+
+//Returns true, if entry includes resource_data_entry
+//Returns false, if entry includes resource_directory
+bool resource_directory_entry::includes_data() const
+{
+ return includes_data_;
+}
+
+//Returns resource_directory if entry includes it, otherwise throws an exception
+const resource_directory& resource_directory_entry::get_resource_directory() const
+{
+ if(!ptr_.dir_ || includes_data_)
+ throw pe_exception("Resource directory entry does not contain resource directory", pe_exception::resource_directory_entry_error);
+
+ return *ptr_.dir_;
+}
+
+//Returns resource_data_entry if entry includes it, otherwise throws an exception
+const resource_data_entry& resource_directory_entry::get_data_entry() const
+{
+ if(!ptr_.data_ || !includes_data_)
+ throw pe_exception("Resource directory entry does not contain resource data entry", pe_exception::resource_directory_entry_error);
+
+ return *ptr_.data_;
+}
+
+//Returns resource_directory if entry includes it, otherwise throws an exception
+resource_directory& resource_directory_entry::get_resource_directory()
+{
+ if(!ptr_.dir_ || includes_data_)
+ throw pe_exception("Resource directory entry does not contain resource directory", pe_exception::resource_directory_entry_error);
+
+ return *ptr_.dir_;
+}
+
+//Returns resource_data_entry if entry includes it, otherwise throws an exception
+resource_data_entry& resource_directory_entry::get_data_entry()
+{
+ if(!ptr_.data_ || !includes_data_)
+ throw pe_exception("Resource directory entry does not contain resource data entry", pe_exception::resource_directory_entry_error);
+
+ return *ptr_.data_;
+}
+
+//Sets entry name
+void resource_directory_entry::set_name(const std::wstring& name)
+{
+ name_ = name;
+ named_ = true;
+ id_ = 0;
+}
+
+//Sets entry ID
+void resource_directory_entry::set_id(uint32_t id)
+{
+ id_ = id;
+ named_ = false;
+ name_.clear();
+}
+
+//Adds resource_data_entry
+void resource_directory_entry::add_data_entry(const resource_data_entry& entry)
+{
+ release();
+ ptr_.data_ = new resource_data_entry(entry);
+ includes_data_ = true;
+}
+
+//Adds resource_directory
+void resource_directory_entry::add_resource_directory(const resource_directory& dir)
+{
+ release();
+ ptr_.dir_ = new resource_directory(dir);
+ includes_data_ = false;
+}
+
+//Default constructor
+resource_directory::resource_directory()
+ :characteristics_(0),
+ timestamp_(0),
+ major_version_(0), minor_version_(0),
+ number_of_named_entries_(0), number_of_id_entries_(0)
+{}
+
+//Constructor from data
+resource_directory::resource_directory(const image_resource_directory& dir)
+ :characteristics_(dir.Characteristics),
+ timestamp_(dir.TimeDateStamp),
+ major_version_(dir.MajorVersion), minor_version_(dir.MinorVersion),
+ number_of_named_entries_(0), number_of_id_entries_(0) //Set to zero here, calculate on add
+{}
+
+//Returns characteristics of directory
+uint32_t resource_directory::get_characteristics() const
+{
+ return characteristics_;
+}
+
+//Returns date and time stamp of directory
+uint32_t resource_directory::get_timestamp() const
+{
+ return timestamp_;
+}
+
+//Returns major version of directory
+uint16_t resource_directory::get_major_version() const
+{
+ return major_version_;
+}
+
+//Returns minor version of directory
+uint16_t resource_directory::get_minor_version() const
+{
+ return minor_version_;
+}
+
+//Returns number of named entries
+uint32_t resource_directory::get_number_of_named_entries() const
+{
+ return number_of_named_entries_;
+}
+
+//Returns number of ID entries
+uint32_t resource_directory::get_number_of_id_entries() const
+{
+ return number_of_id_entries_;
+}
+
+//Returns resource_directory_entry array
+const resource_directory::entry_list& resource_directory::get_entry_list() const
+{
+ return entries_;
+}
+
+//Returns resource_directory_entry array
+resource_directory::entry_list& resource_directory::get_entry_list()
+{
+ return entries_;
+}
+
+//Adds resource_directory_entry
+void resource_directory::add_resource_directory_entry(const resource_directory_entry& entry)
+{
+ entries_.push_back(entry);
+ if(entry.is_named())
+ ++number_of_named_entries_;
+ else
+ ++number_of_id_entries_;
+}
+
+//Clears resource_directory_entry array
+void resource_directory::clear_resource_directory_entry_list()
+{
+ entries_.clear();
+ number_of_named_entries_ = 0;
+ number_of_id_entries_ = 0;
+}
+
+//Sets characteristics of directory
+void resource_directory::set_characteristics(uint32_t characteristics)
+{
+ characteristics_ = characteristics;
+}
+
+//Sets date and time stamp of directory
+void resource_directory::set_timestamp(uint32_t timestamp)
+{
+ timestamp_ = timestamp;
+}
+
+//Sets number of named entries
+void resource_directory::set_number_of_named_entries(uint32_t number)
+{
+ number_of_named_entries_ = number;
+}
+
+//Sets number of ID entries
+void resource_directory::set_number_of_id_entries(uint32_t number)
+{
+ number_of_id_entries_ = number;
+}
+
+//Sets major version of directory
+void resource_directory::set_major_version(uint16_t major_version)
+{
+ major_version_ = major_version;
+}
+
+//Sets minor version of directory
+void resource_directory::get_minor_version(uint16_t minor_version)
+{
+ minor_version_ = minor_version;
+}
+
+//Processes resource directory
+const resource_directory process_resource_directory(const pe_base& pe, uint32_t res_rva, uint32_t offset_to_directory, std::set<uint32_t>& processed)
+{
+ resource_directory ret;
+
+ //Check for resource loops
+ if(!processed.insert(offset_to_directory).second)
+ throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory);
+
+ if(!pe_utils::is_sum_safe(res_rva, offset_to_directory))
+ throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory);
+
+ //Get root IMAGE_RESOURCE_DIRECTORY
+ image_resource_directory directory = pe.section_data_from_rva<image_resource_directory>(res_rva + offset_to_directory, section_data_virtual, true);
+
+ ret = resource_directory(directory);
+
+ //Check DWORDs for possible overflows
+ if(!pe_utils::is_sum_safe(directory.NumberOfIdEntries, directory.NumberOfNamedEntries)
+ || directory.NumberOfIdEntries + directory.NumberOfNamedEntries >= pe_utils::max_dword / sizeof(image_resource_directory_entry) + sizeof(image_resource_directory))
+ throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory);
+
+ if(!pe_utils::is_sum_safe(offset_to_directory, sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry))
+ || !pe_utils::is_sum_safe(res_rva, offset_to_directory + sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry)))
+ throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory);
+
+ for(unsigned long i = 0; i != static_cast<unsigned long>(directory.NumberOfIdEntries) + directory.NumberOfNamedEntries; ++i)
+ {
+ //Read directory entries one by one
+ image_resource_directory_entry dir_entry = pe.section_data_from_rva<image_resource_directory_entry>(
+ res_rva + sizeof(image_resource_directory) + i * sizeof(image_resource_directory_entry) + offset_to_directory, section_data_virtual, true);
+
+ //Create directory entry structure
+ resource_directory_entry entry;
+
+ //If directory is named
+ if(dir_entry.NameIsString)
+ {
+ if(!pe_utils::is_sum_safe(res_rva + sizeof(uint16_t) /* safe */, dir_entry.NameOffset))
+ throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory);
+
+ //get directory name length
+ uint16_t directory_name_length = pe.section_data_from_rva<uint16_t>(res_rva + dir_entry.NameOffset, section_data_virtual, true);
+
+ //Check name length
+ if(pe.section_data_length_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true)
+ < directory_name_length)
+ throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory);
+
+#ifdef PE_BLISS_WINDOWS
+ //Set entry UNICODE name
+ entry.set_name(std::wstring(
+ reinterpret_cast<const wchar_t*>(pe.section_data_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true)),
+ directory_name_length));
+#else
+ //Set entry UNICODE name
+ entry.set_name(pe_utils::from_ucs2(u16string(
+ reinterpret_cast<const unicode16_t*>(pe.section_data_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true)),
+ directory_name_length)));
+#endif
+ }
+ else
+ {
+ //Else - set directory ID
+ entry.set_id(dir_entry.Id);
+ }
+
+ //If directory entry has another resource directory
+ if(dir_entry.DataIsDirectory)
+ {
+ entry.add_resource_directory(process_resource_directory(pe, res_rva, dir_entry.OffsetToDirectory, processed));
+ }
+ else
+ {
+ //If directory entry has data
+ image_resource_data_entry data_entry = pe.section_data_from_rva<image_resource_data_entry>(
+ res_rva + dir_entry.OffsetToData, section_data_virtual, true);
+
+ //Check byte count that stated by data entry
+ if(pe.section_data_length_from_rva(data_entry.OffsetToData, data_entry.OffsetToData, section_data_virtual, true) < data_entry.Size)
+ throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory);
+
+ //Add data entry to directory entry
+ entry.add_data_entry(resource_data_entry(
+ std::string(pe.section_data_from_rva(data_entry.OffsetToData, section_data_virtual, true), data_entry.Size),
+ data_entry.CodePage));
+ }
+
+ //Save directory entry
+ ret.add_resource_directory_entry(entry);
+ }
+
+ //Return resource directory
+ return ret;
+}
+
+//Helper function to calculate needed space for resource data
+void calculate_resource_data_space(const resource_directory& root, uint32_t aligned_offset_from_section_start, uint32_t& needed_size_for_structures, uint32_t& needed_size_for_strings)
+{
+ needed_size_for_structures += sizeof(image_resource_directory);
+ for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it)
+ {
+ needed_size_for_structures += sizeof(image_resource_directory_entry);
+
+ if((*it).is_named())
+ needed_size_for_strings += static_cast<uint32_t>(((*it).get_name().length() + 1) * 2 /* unicode */ + sizeof(uint16_t) /* for string length */);
+
+ if(!(*it).includes_data())
+ calculate_resource_data_space((*it).get_resource_directory(), aligned_offset_from_section_start, needed_size_for_structures, needed_size_for_strings);
+ }
+}
+
+//Helper function to calculate needed space for resource data
+void calculate_resource_data_space(const resource_directory& root, uint32_t needed_size_for_structures, uint32_t needed_size_for_strings, uint32_t& needed_size_for_data, uint32_t& current_data_pos)
+{
+ for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it)
+ {
+ if((*it).includes_data())
+ {
+ uint32_t data_size = static_cast<uint32_t>((*it).get_data_entry().get_data().length()
+ + sizeof(image_resource_data_entry)
+ + (pe_utils::align_up(current_data_pos, sizeof(uint32_t)) - current_data_pos) /* alignment */);
+ needed_size_for_data += data_size;
+ current_data_pos += data_size;
+ }
+ else
+ {
+ calculate_resource_data_space((*it).get_resource_directory(), needed_size_for_structures, needed_size_for_strings, needed_size_for_data, current_data_pos);
+ }
+ }
+}
+
+//Helper: sorts resource directory entries
+struct entry_sorter
+{
+public:
+ bool operator()(const resource_directory_entry& entry1, const resource_directory_entry& entry2) const;
+};
+
+//Helper function to rebuild resource directory
+void rebuild_resource_directory(pe_base& pe, section& resource_section, resource_directory& root, uint32_t& current_structures_pos, uint32_t& current_data_pos, uint32_t& current_strings_pos, uint32_t offset_from_section_start)
+{
+ //Create resource directory
+ image_resource_directory dir = {0};
+ dir.Characteristics = root.get_characteristics();
+ dir.MajorVersion = root.get_major_version();
+ dir.MinorVersion = root.get_minor_version();
+ dir.TimeDateStamp = root.get_timestamp();
+
+ {
+ resource_directory::entry_list& entries = root.get_entry_list();
+ std::sort(entries.begin(), entries.end(), entry_sorter());
+ }
+
+ //Calculate number of named and ID entries
+ for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it)
+ {
+ if((*it).is_named())
+ ++dir.NumberOfNamedEntries;
+ else
+ ++dir.NumberOfIdEntries;
+ }
+
+ std::string& raw_data = resource_section.get_raw_data();
+
+ //Save resource directory
+ memcpy(&raw_data[current_structures_pos], &dir, sizeof(dir));
+ current_structures_pos += sizeof(dir);
+
+ uint32_t this_current_structures_pos = current_structures_pos;
+
+ current_structures_pos += sizeof(image_resource_directory_entry) * (dir.NumberOfNamedEntries + dir.NumberOfIdEntries);
+
+ //Create all resource directory entries
+ for(resource_directory::entry_list::iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it)
+ {
+ image_resource_directory_entry entry;
+ if((*it).is_named())
+ {
+ entry.Name = 0x80000000 | (current_strings_pos - offset_from_section_start);
+ uint16_t unicode_length = static_cast<uint16_t>((*it).get_name().length());
+ memcpy(&raw_data[current_strings_pos], &unicode_length, sizeof(unicode_length));
+ current_strings_pos += sizeof(unicode_length);
+
+#ifdef PE_BLISS_WINDOWS
+ memcpy(&raw_data[current_strings_pos], (*it).get_name().c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */);
+#else
+ {
+ u16string str(pe_utils::to_ucs2((*it).get_name()));
+ memcpy(&raw_data[current_strings_pos], str.c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */);
+ }
+#endif
+
+ current_strings_pos += static_cast<unsigned long>((*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */);
+ }
+ else
+ {
+ entry.Name = (*it).get_id();
+ }
+
+ if((*it).includes_data())
+ {
+ current_data_pos = pe_utils::align_up(current_data_pos, sizeof(uint32_t));
+ image_resource_data_entry data_entry = {0};
+ data_entry.CodePage = (*it).get_data_entry().get_codepage();
+ data_entry.Size = static_cast<uint32_t>((*it).get_data_entry().get_data().length());
+ data_entry.OffsetToData = pe.rva_from_section_offset(resource_section, current_data_pos + sizeof(data_entry));
+
+ entry.OffsetToData = current_data_pos - offset_from_section_start;
+
+ memcpy(&raw_data[current_data_pos], &data_entry, sizeof(data_entry));
+ current_data_pos += sizeof(data_entry);
+
+ memcpy(&raw_data[current_data_pos], (*it).get_data_entry().get_data().data(), data_entry.Size);
+ current_data_pos += data_entry.Size;
+
+ memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry));
+ this_current_structures_pos += sizeof(entry);
+ }
+ else
+ {
+ entry.OffsetToData = 0x80000000 | (current_structures_pos - offset_from_section_start);
+
+ memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry));
+ this_current_structures_pos += sizeof(entry);
+
+ rebuild_resource_directory(pe, resource_section, (*it).get_resource_directory(), current_structures_pos, current_data_pos, current_strings_pos, offset_from_section_start);
+ }
+ }
+}
+
+//Helper function to rebuild resource directory
+bool entry_sorter::operator()(const resource_directory_entry& entry1, const resource_directory_entry& entry2) const
+{
+ if(entry1.is_named() && entry2.is_named())
+ return entry1.get_name() < entry2.get_name();
+ else if(!entry1.is_named() && !entry2.is_named())
+ return entry1.get_id() < entry2.get_id();
+ else
+ return entry1.is_named();
+}
+
+//Resources rebuilder
+//resource_directory - root resource directory
+//resources_section - section where resource directory will be placed (must be attached to PE image)
+//offset_from_section_start - offset from resources_section raw data start
+//resource_directory is non-constant, because it will be sorted
+//save_to_pe_headers - if true, new resource directory information will be saved to PE image headers
+//auto_strip_last_section - if true and resources are placed in the last section, it will be automatically stripped
+//number_of_id_entries and number_of_named_entries for resource directories are recalculated and not used
+const image_directory rebuild_resources(pe_base& pe, resource_directory& info, section& resources_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section)
+{
+ //Check that resources_section is attached to this PE image
+ if(!pe.section_attached(resources_section))
+ throw pe_exception("Resource section must be attached to PE file", pe_exception::section_is_not_attached);
+
+ //Check resource directory correctness
+ if(info.get_entry_list().empty())
+ throw pe_exception("Empty resource directory", pe_exception::incorrect_resource_directory);
+
+ uint32_t aligned_offset_from_section_start = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t));
+ uint32_t needed_size_for_structures = aligned_offset_from_section_start - offset_from_section_start; //Calculate needed size for resource tables and data
+ uint32_t needed_size_for_strings = 0;
+ uint32_t needed_size_for_data = 0;
+
+ calculate_resource_data_space(info, aligned_offset_from_section_start, needed_size_for_structures, needed_size_for_strings);
+
+ {
+ uint32_t current_data_pos = aligned_offset_from_section_start + needed_size_for_structures + needed_size_for_strings;
+ calculate_resource_data_space(info, needed_size_for_structures, needed_size_for_strings, needed_size_for_data, current_data_pos);
+ }
+
+ uint32_t needed_size = needed_size_for_structures + needed_size_for_strings + needed_size_for_data;
+
+ //Check if resources_section is last one. If it's not, check if there's enough place for resource data
+ if(&resources_section != &*(pe.get_image_sections().end() - 1) &&
+ (resources_section.empty() || pe_utils::align_up(resources_section.get_size_of_raw_data(), pe.get_file_alignment())
+ < needed_size + aligned_offset_from_section_start))
+ throw pe_exception("Insufficient space for resource directory", pe_exception::insufficient_space);
+
+ std::string& raw_data = resources_section.get_raw_data();
+
+ //This will be done only if resources_section is the last section of image or for section with unaligned raw length of data
+ if(raw_data.length() < needed_size + aligned_offset_from_section_start)
+ raw_data.resize(needed_size + aligned_offset_from_section_start); //Expand section raw data
+
+ uint32_t current_structures_pos = aligned_offset_from_section_start;
+ uint32_t current_strings_pos = current_structures_pos + needed_size_for_structures;
+ uint32_t current_data_pos = current_strings_pos + needed_size_for_strings;
+ rebuild_resource_directory(pe, resources_section, info, current_structures_pos, current_data_pos, current_strings_pos, aligned_offset_from_section_start);
+
+ //Adjust section raw and virtual sizes
+ pe.recalculate_section_sizes(resources_section, auto_strip_last_section);
+
+ image_directory ret(pe.rva_from_section_offset(resources_section, aligned_offset_from_section_start), needed_size);
+
+ //If auto-rewrite of PE headers is required
+ if(save_to_pe_header)
+ {
+ pe.set_directory_rva(image_directory_entry_resource, ret.get_rva());
+ pe.set_directory_size(image_directory_entry_resource, ret.get_size());
+ }
+
+ return ret;
+}
+
+//Returns resources from PE file
+const resource_directory get_resources(const pe_base& pe)
+{
+ resource_directory ret;
+
+ if(!pe.has_resources())
+ return ret;
+
+ //Get resource directory RVA
+ uint32_t res_rva = pe.get_directory_rva(image_directory_entry_resource);
+
+ //Store already processed directories to avoid resource loops
+ std::set<uint32_t> processed;
+
+ //Process all directories (recursion)
+ ret = process_resource_directory(pe, res_rva, 0, processed);
+
+ return ret;
+}
+
+//Finds resource_directory_entry by ID
+resource_directory::id_entry_finder::id_entry_finder(uint32_t id)
+ :id_(id)
+{}
+
+bool resource_directory::id_entry_finder::operator()(const resource_directory_entry& entry) const
+{
+ return !entry.is_named() && entry.get_id() == id_;
+}
+
+//Finds resource_directory_entry by name
+resource_directory::name_entry_finder::name_entry_finder(const std::wstring& name)
+ :name_(name)
+{}
+
+bool resource_directory::name_entry_finder::operator()(const resource_directory_entry& entry) const
+{
+ return entry.is_named() && entry.get_name() == name_;
+}
+
+//Finds resource_directory_entry by name or ID (universal)
+resource_directory::entry_finder::entry_finder(const std::wstring& name)
+ :name_(name), named_(true)
+{}
+
+resource_directory::entry_finder::entry_finder(uint32_t id)
+ :id_(id), named_(false)
+{}
+
+bool resource_directory::entry_finder::operator()(const resource_directory_entry& entry) const
+{
+ if(named_)
+ return entry.is_named() && entry.get_name() == name_;
+ else
+ return !entry.is_named() && entry.get_id() == id_;
+}
+
+//Returns resource_directory_entry by ID. If not found - throws an exception
+const resource_directory_entry& resource_directory::entry_by_id(uint32_t id) const
+{
+ entry_list::const_iterator i = std::find_if(entries_.begin(), entries_.end(), id_entry_finder(id));
+ if(i == entries_.end())
+ throw pe_exception("Resource directory entry not found", pe_exception::resource_directory_entry_not_found);
+
+ return *i;
+}
+
+//Returns resource_directory_entry by name. If not found - throws an exception
+const resource_directory_entry& resource_directory::entry_by_name(const std::wstring& name) const
+{
+ entry_list::const_iterator i = std::find_if(entries_.begin(), entries_.end(), name_entry_finder(name));
+ if(i == entries_.end())
+ throw pe_exception("Resource directory entry not found", pe_exception::resource_directory_entry_not_found);
+
+ return *i;
+}
+}
diff --git a/tools/pe_bliss/pe_resources.h b/tools/pe_bliss/pe_resources.h
new file mode 100644
index 0000000000..1eb6437563
--- /dev/null
+++ b/tools/pe_bliss/pe_resources.h
@@ -0,0 +1,245 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <vector>
+#include <string>
+#include <set>
+#include "pe_structures.h"
+#include "pe_base.h"
+#include "pe_directory.h"
+
+namespace pe_bliss
+{
+//Class representing resource data entry
+class resource_data_entry
+{
+public:
+ //Default constructor
+ resource_data_entry();
+ //Constructor from data
+ resource_data_entry(const std::string& data, uint32_t codepage);
+
+ //Returns resource data codepage
+ uint32_t get_codepage() const;
+ //Returns resource data
+ const std::string& get_data() const;
+
+public: //These functions do not change everything inside image, they are used by PE class
+ //You can also use them to rebuild resource directory
+
+ //Sets resource data codepage
+ void set_codepage(uint32_t codepage);
+ //Sets resource data
+ void set_data(const std::string& data);
+
+private:
+ uint32_t codepage_; //Resource data codepage
+ std::string data_; //Resource data
+};
+
+//Forward declaration
+class resource_directory;
+
+//Class representing resource directory entry
+class resource_directory_entry
+{
+public:
+ //Default constructor
+ resource_directory_entry();
+ //Copy constructor
+ resource_directory_entry(const resource_directory_entry& other);
+ //Copy assignment operator
+ resource_directory_entry& operator=(const resource_directory_entry& other);
+
+ //Returns entry ID
+ uint32_t get_id() const;
+ //Returns entry name
+ const std::wstring& get_name() const;
+ //Returns true, if entry has name
+ //Returns false, if entry has ID
+ bool is_named() const;
+
+ //Returns true, if entry includes resource_data_entry
+ //Returns false, if entry includes resource_directory
+ bool includes_data() const;
+ //Returns resource_directory if entry includes it, otherwise throws an exception
+ const resource_directory& get_resource_directory() const;
+ //Returns resource_data_entry if entry includes it, otherwise throws an exception
+ const resource_data_entry& get_data_entry() const;
+
+ //Destructor
+ ~resource_directory_entry();
+
+public: //These functions do not change everything inside image, they are used by PE class
+ //You can also use them to rebuild resource directory
+
+ //Sets entry name
+ void set_name(const std::wstring& name);
+ //Sets entry ID
+ void set_id(uint32_t id);
+
+ //Returns resource_directory if entry includes it, otherwise throws an exception
+ resource_directory& get_resource_directory();
+ //Returns resource_data_entry if entry includes it, otherwise throws an exception
+ resource_data_entry& get_data_entry();
+
+ //Adds resource_data_entry
+ void add_data_entry(const resource_data_entry& entry);
+ //Adds resource_directory
+ void add_resource_directory(const resource_directory& dir);
+
+private:
+ //Destroys included data
+ void release();
+
+private:
+ uint32_t id_;
+ std::wstring name_;
+
+ union includes
+ {
+ //Default constructor
+ includes();
+
+ //We use pointers, we're doing manual copying here
+ class resource_data_entry* data_;
+ class resource_directory* dir_; //We use pointer, because structs include each other
+ };
+
+ includes ptr_;
+
+ bool includes_data_, named_;
+};
+
+//Class representing resource directory
+class resource_directory
+{
+public:
+ typedef std::vector<resource_directory_entry> entry_list;
+
+public:
+ //Default constructor
+ resource_directory();
+ //Constructor from data
+ explicit resource_directory(const pe_win::image_resource_directory& dir);
+
+ //Returns characteristics of directory
+ uint32_t get_characteristics() const;
+ //Returns date and time stamp of directory
+ uint32_t get_timestamp() const;
+ //Returns number of named entries
+ uint32_t get_number_of_named_entries() const;
+ //Returns number of ID entries
+ uint32_t get_number_of_id_entries() const;
+ //Returns major version of directory
+ uint16_t get_major_version() const;
+ //Returns minor version of directory
+ uint16_t get_minor_version() const;
+ //Returns resource_directory_entry array
+ const entry_list& get_entry_list() const;
+ //Returns resource_directory_entry by ID. If not found - throws an exception
+ const resource_directory_entry& entry_by_id(uint32_t id) const;
+ //Returns resource_directory_entry by name. If not found - throws an exception
+ const resource_directory_entry& entry_by_name(const std::wstring& name) const;
+
+public: //These functions do not change everything inside image, they are used by PE class
+ //You can also use them to rebuild resource directory
+
+ //Adds resource_directory_entry
+ void add_resource_directory_entry(const resource_directory_entry& entry);
+ //Clears resource_directory_entry array
+ void clear_resource_directory_entry_list();
+
+ //Sets characteristics of directory
+ void set_characteristics(uint32_t characteristics);
+ //Sets date and time stamp of directory
+ void set_timestamp(uint32_t timestamp);
+ //Sets number of named entries
+ void set_number_of_named_entries(uint32_t number);
+ //Sets number of ID entries
+ void set_number_of_id_entries(uint32_t number);
+ //Sets major version of directory
+ void set_major_version(uint16_t major_version);
+ //Sets minor version of directory
+ void get_minor_version(uint16_t minor_version);
+
+ //Returns resource_directory_entry array
+ entry_list& get_entry_list();
+
+private:
+ uint32_t characteristics_;
+ uint32_t timestamp_;
+ uint16_t major_version_, minor_version_;
+ uint32_t number_of_named_entries_, number_of_id_entries_;
+ entry_list entries_;
+
+public: //Finder helpers
+ //Finds resource_directory_entry by ID
+ struct id_entry_finder
+ {
+ public:
+ explicit id_entry_finder(uint32_t id);
+ bool operator()(const resource_directory_entry& entry) const;
+
+ private:
+ uint32_t id_;
+ };
+
+ //Finds resource_directory_entry by name
+ struct name_entry_finder
+ {
+ public:
+ explicit name_entry_finder(const std::wstring& name);
+ bool operator()(const resource_directory_entry& entry) const;
+
+ private:
+ std::wstring name_;
+ };
+
+ //Finds resource_directory_entry by name or ID (universal)
+ struct entry_finder
+ {
+ public:
+ explicit entry_finder(const std::wstring& name);
+ explicit entry_finder(uint32_t id);
+ bool operator()(const resource_directory_entry& entry) const;
+
+ private:
+ std::wstring name_;
+ uint32_t id_;
+ bool named_;
+ };
+};
+
+//Returns resources (root resource_directory) from PE file
+const resource_directory get_resources(const pe_base& pe);
+
+//Resources rebuilder
+//resource_directory - root resource directory
+//resources_section - section where resource directory will be placed (must be attached to PE image)
+//resource_directory is non-constant, because it will be sorted
+//offset_from_section_start - offset from resources_section raw data start
+//save_to_pe_headers - if true, new resource directory information will be saved to PE image headers
+//auto_strip_last_section - if true and resources are placed in the last section, it will be automatically stripped
+//number_of_id_entries and number_of_named_entries for resource directories are recalculated and not used
+const image_directory rebuild_resources(pe_base& pe, resource_directory& info, section& resources_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true);
+}
diff --git a/tools/pe_bliss/pe_rich_data.cpp b/tools/pe_bliss/pe_rich_data.cpp
new file mode 100644
index 0000000000..e92f7ddc1b
--- /dev/null
+++ b/tools/pe_bliss/pe_rich_data.cpp
@@ -0,0 +1,152 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "pe_rich_data.h"
+
+namespace pe_bliss
+{
+//STUB OVERLAY
+//Default constructor
+rich_data::rich_data()
+ :number_(0), version_(0), times_(0)
+{}
+
+//Who knows, what these fields mean...
+uint32_t rich_data::get_number() const
+{
+ return number_;
+}
+
+uint32_t rich_data::get_version() const
+{
+ return version_;
+}
+
+uint32_t rich_data::get_times() const
+{
+ return times_;
+}
+
+void rich_data::set_number(uint32_t number)
+{
+ number_ = number;
+}
+
+void rich_data::set_version(uint32_t version)
+{
+ version_ = version;
+}
+
+void rich_data::set_times(uint32_t times)
+{
+ times_ = times;
+}
+
+//Returns MSVC rich data
+const rich_data_list get_rich_data(const pe_base& pe)
+{
+ //Returned value
+ rich_data_list ret;
+
+ const std::string& rich_overlay = pe.get_stub_overlay();
+
+ //If there's no rich overlay, return empty vector
+ if(rich_overlay.size() < sizeof(uint32_t))
+ return ret;
+
+ //True if rich data was found
+ bool found = false;
+
+ //Rich overlay ID ("Rich" word)
+ static const uint32_t rich_overlay_id = 0x68636952;
+
+ //Search for rich data overlay ID
+ const char* begin = &rich_overlay[0];
+ const char* end = begin + rich_overlay.length();
+ for(; begin != end; ++begin)
+ {
+ if(*reinterpret_cast<const uint32_t*>(begin) == rich_overlay_id)
+ {
+ found = true; //We've found it!
+ break;
+ }
+ }
+
+ //If we found it
+ if(found)
+ {
+ //Check remaining length
+ if(static_cast<size_t>(end - begin) < sizeof(uint32_t))
+ return ret;
+
+ //The XOR key is after "Rich" word, we should get it
+ uint32_t xorkey = *reinterpret_cast<const uint32_t*>(begin + sizeof(uint32_t));
+
+ //True if rich data was found
+ found = false;
+
+ //Second search for signature "DanS"
+ begin = &rich_overlay[0];
+ for(; begin != end; ++begin)
+ {
+ if((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) == 0x536e6144) //"DanS"
+ {
+ found = true;
+ break;
+ }
+ }
+
+ //If second signature is found
+ if(found)
+ {
+ begin += sizeof(uint32_t) * 3;
+ //List all rich data structures
+ while(begin < end)
+ {
+ begin += sizeof(uint32_t);
+ if(begin >= end)
+ break;
+
+ //Check for rich overlay data end ("Rich" word reached)
+ if(*reinterpret_cast<const uint32_t*>(begin) == rich_overlay_id)
+ break;
+
+ //Create rich_data structure
+ rich_data data;
+ data.set_number((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) >> 16);
+ data.set_version((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) & 0xFFFF);
+
+ begin += sizeof(uint32_t);
+ if(begin >= end)
+ break;
+
+ data.set_times(*reinterpret_cast<const uint32_t*>(begin) ^ xorkey);
+
+ //Save rich data structure
+ ret.push_back(data);
+ }
+ }
+ }
+
+ //Return rich data structures list
+ return ret;
+}
+}
diff --git a/tools/pe_bliss/pe_rich_data.h b/tools/pe_bliss/pe_rich_data.h
new file mode 100644
index 0000000000..3e01d3c011
--- /dev/null
+++ b/tools/pe_bliss/pe_rich_data.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <vector>
+#include "pe_structures.h"
+#include "pe_base.h"
+
+namespace pe_bliss
+{
+//Rich data overlay class of Microsoft Visual Studio
+class rich_data
+{
+public:
+ //Default constructor
+ rich_data();
+
+public: //Getters
+ //Who knows, what these fields mean...
+ uint32_t get_number() const;
+ uint32_t get_version() const;
+ uint32_t get_times() const;
+
+public: //Setters, used by PE library only
+ void set_number(uint32_t number);
+ void set_version(uint32_t version);
+ void set_times(uint32_t times);
+
+private:
+ uint32_t number_;
+ uint32_t version_;
+ uint32_t times_;
+};
+
+//Rich data list typedef
+typedef std::vector<rich_data> rich_data_list;
+
+//Returns a vector with rich data (stub overlay)
+const rich_data_list get_rich_data(const pe_base& pe);
+}
diff --git a/tools/pe_bliss/pe_section.cpp b/tools/pe_bliss/pe_section.cpp
new file mode 100644
index 0000000000..72127e22e2
--- /dev/null
+++ b/tools/pe_bliss/pe_section.cpp
@@ -0,0 +1,303 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <algorithm>
+#include <string.h>
+#include "utils.h"
+#include "pe_section.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//Section structure default constructor
+section::section()
+ :old_size_(static_cast<size_t>(-1))
+{
+ memset(&header_, 0, sizeof(image_section_header));
+}
+
+//Sets the name of section (8 characters maximum)
+void section::set_name(const std::string& name)
+{
+ memset(header_.Name, 0, sizeof(header_.Name));
+ memcpy(header_.Name, name.c_str(), std::min<size_t>(name.length(), sizeof(header_.Name)));
+}
+
+//Returns section name
+const std::string section::get_name() const
+{
+ char buf[9] = {0};
+ memcpy(buf, header_.Name, 8);
+ return std::string(buf);
+}
+
+//Set flag (attribute) of section
+section& section::set_flag(uint32_t flag, bool setflag)
+{
+ if(setflag)
+ header_.Characteristics |= flag;
+ else
+ header_.Characteristics &= ~flag;
+
+ return *this;
+}
+
+//Sets "readable" attribute of section
+section& section::readable(bool readable)
+{
+ return set_flag(image_scn_mem_read, readable);
+}
+
+//Sets "writeable" attribute of section
+section& section::writeable(bool writeable)
+{
+ return set_flag(image_scn_mem_write, writeable);
+}
+
+//Sets "executable" attribute of section
+section& section::executable(bool executable)
+{
+ return set_flag(image_scn_mem_execute, executable);
+}
+
+//Sets "shared" attribute of section
+section& section::shared(bool shared)
+{
+ return set_flag(image_scn_mem_shared, shared);
+}
+
+//Sets "discardable" attribute of section
+section& section::discardable(bool discardable)
+{
+ return set_flag(image_scn_mem_discardable, discardable);
+}
+
+//Returns true if section is readable
+bool section::readable() const
+{
+ return (header_.Characteristics & image_scn_mem_read) != 0;
+}
+
+//Returns true if section is writeable
+bool section::writeable() const
+{
+ return (header_.Characteristics & image_scn_mem_write) != 0;
+}
+
+//Returns true if section is executable
+bool section::executable() const
+{
+ return (header_.Characteristics & image_scn_mem_execute) != 0;
+}
+
+bool section::shared() const
+{
+ return (header_.Characteristics & image_scn_mem_shared) != 0;
+}
+
+bool section::discardable() const
+{
+ return (header_.Characteristics & image_scn_mem_discardable) != 0;
+}
+
+//Returns true if section has no RAW data
+bool section::empty() const
+{
+ if(old_size_ != static_cast<size_t>(-1)) //If virtual memory is mapped, check raw data length (old_size_)
+ return old_size_ == 0;
+ else
+ return raw_data_.empty();
+}
+
+//Returns raw section data from file image
+std::string& section::get_raw_data()
+{
+ unmap_virtual();
+ return raw_data_;
+}
+
+//Sets raw section data from file image
+void section::set_raw_data(const std::string& data)
+{
+ old_size_ = static_cast<size_t>(-1);
+ raw_data_ = data;
+}
+
+//Returns raw section data from file image
+const std::string& section::get_raw_data() const
+{
+ unmap_virtual();
+ return raw_data_;
+}
+
+//Returns mapped virtual section data
+const std::string& section::get_virtual_data(uint32_t section_alignment) const
+{
+ map_virtual(section_alignment);
+ return raw_data_;
+}
+
+//Returns mapped virtual section data
+std::string& section::get_virtual_data(uint32_t section_alignment)
+{
+ map_virtual(section_alignment);
+ return raw_data_;
+}
+
+//Maps virtual section data
+void section::map_virtual(uint32_t section_alignment) const
+{
+ uint32_t aligned_virtual_size = get_aligned_virtual_size(section_alignment);
+ if(old_size_ == static_cast<size_t>(-1) && aligned_virtual_size && aligned_virtual_size > raw_data_.length())
+ {
+ old_size_ = raw_data_.length();
+ raw_data_.resize(aligned_virtual_size, 0);
+ }
+}
+
+//Unmaps virtual section data
+void section::unmap_virtual() const
+{
+ if(old_size_ != static_cast<size_t>(-1))
+ {
+ raw_data_.resize(old_size_, 0);
+ old_size_ = static_cast<size_t>(-1);
+ }
+}
+
+//Returns section virtual size
+uint32_t section::get_virtual_size() const
+{
+ return header_.Misc.VirtualSize;
+}
+
+//Returns section virtual address
+uint32_t section::get_virtual_address() const
+{
+ return header_.VirtualAddress;
+}
+
+//Returns size of section raw data
+uint32_t section::get_size_of_raw_data() const
+{
+ return header_.SizeOfRawData;
+}
+
+//Returns pointer to raw section data in PE file
+uint32_t section::get_pointer_to_raw_data() const
+{
+ return header_.PointerToRawData;
+}
+
+//Returns section characteristics
+uint32_t section::get_characteristics() const
+{
+ return header_.Characteristics;
+}
+
+//Returns raw image section header
+const pe_win::image_section_header& section::get_raw_header() const
+{
+ return header_;
+}
+
+//Returns raw image section header
+pe_win::image_section_header& section::get_raw_header()
+{
+ return header_;
+}
+
+//Calculates aligned virtual section size
+uint32_t section::get_aligned_virtual_size(uint32_t section_alignment) const
+{
+ if(get_size_of_raw_data())
+ {
+ if(!get_virtual_size())
+ {
+ //If section virtual size is zero
+ //Set aligned virtual size of section as aligned raw size
+ return pe_utils::align_up(get_size_of_raw_data(), section_alignment);
+ }
+ }
+
+ return pe_utils::align_up(get_virtual_size(), section_alignment);
+}
+
+//Calculates aligned raw section size
+uint32_t section::get_aligned_raw_size(uint32_t file_alignment) const
+{
+ if(get_size_of_raw_data())
+ return pe_utils::align_up(get_size_of_raw_data(), file_alignment);
+ else
+ return 0;
+}
+
+//Sets size of raw section data
+void section::set_size_of_raw_data(uint32_t size_of_raw_data)
+{
+ header_.SizeOfRawData = size_of_raw_data;
+}
+
+//Sets pointer to section raw data
+void section::set_pointer_to_raw_data(uint32_t pointer_to_raw_data)
+{
+ header_.PointerToRawData = pointer_to_raw_data;
+}
+
+//Sets section characteristics
+void section::set_characteristics(uint32_t characteristics)
+{
+ header_.Characteristics = characteristics;
+}
+
+//Sets section virtual size
+void section::set_virtual_size(uint32_t virtual_size)
+{
+ header_.Misc.VirtualSize = virtual_size;
+}
+
+//Sets section virtual address
+void section::set_virtual_address(uint32_t virtual_address)
+{
+ header_.VirtualAddress = virtual_address;
+}
+
+//Section by file offset finder helper (4gb max)
+section_by_raw_offset::section_by_raw_offset(uint32_t offset)
+ :offset_(offset)
+{}
+
+bool section_by_raw_offset::operator()(const section& s) const
+{
+ return (s.get_pointer_to_raw_data() <= offset_)
+ && (s.get_pointer_to_raw_data() + s.get_size_of_raw_data() > offset_);
+}
+
+section_ptr_finder::section_ptr_finder(const section& s)
+ :s_(s)
+{}
+
+bool section_ptr_finder::operator()(const section& s) const
+{
+ return &s == &s_;
+}
+}
diff --git a/tools/pe_bliss/pe_section.h b/tools/pe_bliss/pe_section.h
new file mode 100644
index 0000000000..617ecebe26
--- /dev/null
+++ b/tools/pe_bliss/pe_section.h
@@ -0,0 +1,158 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <string>
+#include <vector>
+#include "pe_structures.h"
+
+namespace pe_bliss
+{
+//Enumeration of section data types, used in functions below
+enum section_data_type
+{
+ section_data_raw,
+ section_data_virtual
+};
+
+//Class representing image section
+class section
+{
+public:
+ //Default constructor
+ section();
+
+ //Sets the name of section (stripped to 8 characters)
+ void set_name(const std::string& name);
+
+ //Returns the name of section
+ const std::string get_name() const;
+
+ //Changes attributes of section
+ section& readable(bool readable);
+ section& writeable(bool writeable);
+ section& executable(bool executable);
+ section& shared(bool shared);
+ section& discardable(bool discardable);
+
+ //Returns attributes of section
+ bool readable() const;
+ bool writeable() const;
+ bool executable() const;
+ bool shared() const;
+ bool discardable() const;
+
+ //Returns true if section has no RAW data
+ bool empty() const;
+
+ //Returns raw section data from file image
+ std::string& get_raw_data();
+ //Returns raw section data from file image
+ const std::string& get_raw_data() const;
+ //Returns mapped virtual section data
+ const std::string& get_virtual_data(uint32_t section_alignment) const;
+ //Returns mapped virtual section data
+ std::string& get_virtual_data(uint32_t section_alignment);
+
+public: //Header getters
+ //Returns section virtual size
+ uint32_t get_virtual_size() const;
+ //Returns section virtual address (RVA)
+ uint32_t get_virtual_address() const;
+ //Returns size of section raw data
+ uint32_t get_size_of_raw_data() const;
+ //Returns pointer to raw section data in PE file
+ uint32_t get_pointer_to_raw_data() const;
+ //Returns section characteristics
+ uint32_t get_characteristics() const;
+
+ //Returns raw image section header
+ const pe_win::image_section_header& get_raw_header() const;
+
+public: //Aligned sizes calculation
+ //Calculates aligned virtual section size
+ uint32_t get_aligned_virtual_size(uint32_t section_alignment) const;
+ //Calculates aligned raw section size
+ uint32_t get_aligned_raw_size(uint32_t file_alignment) const;
+
+public: //Setters
+ //Sets size of raw section data
+ void set_size_of_raw_data(uint32_t size_of_raw_data);
+ //Sets pointer to section raw data
+ void set_pointer_to_raw_data(uint32_t pointer_to_raw_data);
+ //Sets section characteristics
+ void set_characteristics(uint32_t characteristics);
+ //Sets raw section data from file image
+ void set_raw_data(const std::string& data);
+
+public: //Setters, be careful
+ //Sets section virtual size (doesn't set internal aligned virtual size, changes only header value)
+ //Better use pe_base::set_section_virtual_size
+ void set_virtual_size(uint32_t virtual_size);
+ //Sets section virtual address
+ void set_virtual_address(uint32_t virtual_address);
+ //Returns raw image section header
+ pe_win::image_section_header& get_raw_header();
+
+private:
+ //Section header
+ pe_win::image_section_header header_;
+
+ //Maps virtual section data
+ void map_virtual(uint32_t section_alignment) const;
+
+ //Unmaps virtual section data
+ void unmap_virtual() const;
+
+ //Set flag (attribute) of section
+ section& set_flag(uint32_t flag, bool setflag);
+
+ //Old size of section (stored after mapping of virtual section memory)
+ mutable std::size_t old_size_;
+
+ //Section raw/virtual data
+ mutable std::string raw_data_;
+};
+
+//Section by file offset finder helper (4gb max)
+struct section_by_raw_offset
+{
+public:
+ explicit section_by_raw_offset(uint32_t offset);
+ bool operator()(const section& s) const;
+
+private:
+ uint32_t offset_;
+};
+
+//Helper: finder of section* in sections list
+struct section_ptr_finder
+{
+public:
+ explicit section_ptr_finder(const section& s);
+ bool operator()(const section& s) const;
+
+private:
+ const section& s_;
+};
+
+typedef std::vector<section> section_list;
+}
diff --git a/tools/pe_bliss/pe_structures.h b/tools/pe_bliss/pe_structures.h
new file mode 100644
index 0000000000..efc99103b2
--- /dev/null
+++ b/tools/pe_bliss/pe_structures.h
@@ -0,0 +1,1028 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <string>
+#include <sstream>
+#include "stdint_defs.h"
+#if defined(_MSC_VER) or defined(__MINGW32__)
+#define PE_BLISS_WINDOWS
+#endif
+
+namespace pe_bliss
+{
+//Enumeration of PE types
+enum pe_type
+{
+ pe_type_32,
+ pe_type_64
+};
+
+namespace pe_win
+{
+const uint32_t image_numberof_directory_entries = 16;
+const uint32_t image_nt_optional_hdr32_magic = 0x10b;
+const uint32_t image_nt_optional_hdr64_magic = 0x20b;
+const uint32_t image_resource_name_is_string = 0x80000000;
+const uint32_t image_resource_data_is_directory = 0x80000000;
+
+const uint32_t image_dllcharacteristics_dynamic_base = 0x0040; // DLL can move.
+const uint32_t image_dllcharacteristics_force_integrity = 0x0080; // Code Integrity Image
+const uint32_t image_dllcharacteristics_nx_compat = 0x0100; // Image is NX compatible
+const uint32_t image_dllcharacteristics_no_isolation = 0x0200; // Image understands isolation and doesn't want it
+const uint32_t image_dllcharacteristics_no_seh = 0x0400; // Image does not use SEH. No SE handler may reside in this image
+const uint32_t image_dllcharacteristics_no_bind = 0x0800; // Do not bind this image.
+const uint32_t image_dllcharacteristics_wdm_driver = 0x2000; // Driver uses WDM model
+const uint32_t image_dllcharacteristics_terminal_server_aware = 0x8000;
+
+const uint32_t image_sizeof_file_header = 20;
+
+const uint32_t image_file_relocs_stripped = 0x0001; // Relocation info stripped from file.
+const uint32_t image_file_executable_image = 0x0002; // File is executable (i.e. no unresolved externel references).
+const uint32_t image_file_line_nums_stripped = 0x0004; // Line nunbers stripped from file.
+const uint32_t image_file_local_syms_stripped = 0x0008; // Local symbols stripped from file.
+const uint32_t image_file_aggresive_ws_trim = 0x0010; // Agressively trim working set
+const uint32_t image_file_large_address_aware = 0x0020; // App can handle >2gb addresses
+const uint32_t image_file_bytes_reversed_lo = 0x0080; // Bytes of machine word are reversed.
+const uint32_t image_file_32bit_machine = 0x0100; // 32 bit word machine.
+const uint32_t image_file_debug_stripped = 0x0200; // Debugging info stripped from file in .DBG file
+const uint32_t image_file_removable_run_from_swap = 0x0400; // If Image is on removable media, copy and run from the swap file.
+const uint32_t image_file_net_run_from_swap = 0x0800; // If Image is on Net, copy and run from the swap file.
+const uint32_t image_file_system = 0x1000; // System File.
+const uint32_t image_file_dll = 0x2000; // File is a DLL.
+const uint32_t image_file_up_system_only = 0x4000; // File should only be run on a UP machine
+const uint32_t image_file_bytes_reversed_hi = 0x8000; // Bytes of machine word are reversed.
+
+const uint32_t image_scn_lnk_nreloc_ovfl = 0x01000000; // Section contains extended relocations.
+const uint32_t image_scn_mem_discardable = 0x02000000; // Section can be discarded.
+const uint32_t image_scn_mem_not_cached = 0x04000000; // Section is not cachable.
+const uint32_t image_scn_mem_not_paged = 0x08000000; // Section is not pageable.
+const uint32_t image_scn_mem_shared = 0x10000000; // Section is shareable.
+const uint32_t image_scn_mem_execute = 0x20000000; // Section is executable.
+const uint32_t image_scn_mem_read = 0x40000000; // Section is readable.
+const uint32_t image_scn_mem_write = 0x80000000; // Section is writeable.
+
+const uint32_t image_scn_cnt_code = 0x00000020; // Section contains code.
+const uint32_t image_scn_cnt_initialized_data = 0x00000040; // Section contains initialized data.
+const uint32_t image_scn_cnt_uninitialized_data = 0x00000080; // Section contains uninitialized data.
+
+//Directory Entries
+const uint32_t image_directory_entry_export = 0; // Export Directory
+const uint32_t image_directory_entry_import = 1; // Import Directory
+const uint32_t image_directory_entry_resource = 2; // Resource Directory
+const uint32_t image_directory_entry_exception = 3; // Exception Directory
+const uint32_t image_directory_entry_security = 4; // Security Directory
+const uint32_t image_directory_entry_basereloc = 5; // Base Relocation Table
+const uint32_t image_directory_entry_debug = 6; // Debug Directory
+const uint32_t image_directory_entry_architecture = 7; // Architecture Specific Data
+const uint32_t image_directory_entry_globalptr = 8; // RVA of GP
+const uint32_t image_directory_entry_tls = 9; // TLS Directory
+const uint32_t image_directory_entry_load_config = 10; // Load Configuration Directory
+const uint32_t image_directory_entry_bound_import = 11; // Bound Import Directory in headers
+const uint32_t image_directory_entry_iat = 12; // Import Address Table
+const uint32_t image_directory_entry_delay_import = 13; // Delay Load Import Descriptors
+const uint32_t image_directory_entry_com_descriptor = 14; // COM Runtime descriptor
+
+//Subsystem Values
+const uint32_t image_subsystem_unknown = 0; // Unknown subsystem.
+const uint32_t image_subsystem_native = 1; // Image doesn't require a subsystem.
+const uint32_t image_subsystem_windows_gui = 2; // Image runs in the Windows GUI subsystem.
+const uint32_t image_subsystem_windows_cui = 3; // Image runs in the Windows character subsystem.
+const uint32_t image_subsystem_os2_cui = 5; // image runs in the OS/2 character subsystem.
+const uint32_t image_subsystem_posix_cui = 7; // image runs in the Posix character subsystem.
+const uint32_t image_subsystem_native_windows = 8; // image is a native Win9x driver.
+const uint32_t image_subsystem_windows_ce_gui = 9; // Image runs in the Windows CE subsystem.
+const uint32_t image_subsystem_efi_application = 10; //
+const uint32_t image_subsystem_efi_boot_service_driver = 11; //
+const uint32_t image_subsystem_efi_runtime_driver = 12; //
+const uint32_t image_subsystem_efi_rom = 13;
+const uint32_t image_subsystem_xbox = 14;
+const uint32_t image_subsystem_windows_boot_application = 16;
+
+//Imports
+const uint64_t image_ordinal_flag64 = 0x8000000000000000ull;
+const uint32_t image_ordinal_flag32 = 0x80000000;
+
+//Based relocation types
+const uint32_t image_rel_based_absolute = 0;
+const uint32_t image_rel_based_high = 1;
+const uint32_t image_rel_based_low = 2;
+const uint32_t image_rel_based_highlow = 3;
+const uint32_t image_rel_based_highadj = 4;
+const uint32_t image_rel_based_mips_jmpaddr = 5;
+const uint32_t image_rel_based_mips_jmpaddr16 = 9;
+const uint32_t image_rel_based_ia64_imm64 = 9;
+const uint32_t image_rel_based_dir64 = 10;
+
+//Exception directory
+//The function has an exception handler that should be called when looking for functions that need to examine exceptions
+const uint32_t unw_flag_ehandler = 0x01;
+//The function has a termination handler that should be called when unwinding an exception
+const uint32_t unw_flag_uhandler = 0x02;
+//This unwind info structure is not the primary one for the procedure.
+//Instead, the chained unwind info entry is the contents of a previous RUNTIME_FUNCTION entry.
+//If this flag is set, then the UNW_FLAG_EHANDLER and UNW_FLAG_UHANDLER flags must be cleared.
+//Also, the frame register and fixed-stack allocation fields must have the same values as in the primary unwind info
+const uint32_t unw_flag_chaininfo = 0x04;
+
+//Debug
+const uint32_t image_debug_misc_exename = 1;
+const uint32_t image_debug_type_unknown = 0;
+const uint32_t image_debug_type_coff = 1;
+const uint32_t image_debug_type_codeview = 2;
+const uint32_t image_debug_type_fpo = 3;
+const uint32_t image_debug_type_misc = 4;
+const uint32_t image_debug_type_exception = 5;
+const uint32_t image_debug_type_fixup = 6;
+const uint32_t image_debug_type_omap_to_src = 7;
+const uint32_t image_debug_type_omap_from_src = 8;
+const uint32_t image_debug_type_borland = 9;
+const uint32_t image_debug_type_reserved10 = 10;
+const uint32_t image_debug_type_clsid = 11;
+
+
+//Storage classes
+const uint32_t image_sym_class_end_of_function = static_cast<uint8_t>(-1);
+const uint32_t image_sym_class_null = 0x0000;
+const uint32_t image_sym_class_automatic = 0x0001;
+const uint32_t image_sym_class_external = 0x0002;
+const uint32_t image_sym_class_static = 0x0003;
+const uint32_t image_sym_class_register = 0x0004;
+const uint32_t image_sym_class_external_def = 0x0005;
+const uint32_t image_sym_class_label = 0x0006;
+const uint32_t image_sym_class_undefined_label = 0x0007;
+const uint32_t image_sym_class_member_of_struct = 0x0008;
+const uint32_t image_sym_class_argument = 0x0009;
+const uint32_t image_sym_class_struct_tag = 0x000a;
+const uint32_t image_sym_class_member_of_union = 0x000b;
+const uint32_t image_sym_class_union_tag = 0x000c;
+const uint32_t image_sym_class_type_definition = 0x000d;
+const uint32_t image_sym_class_undefined_static = 0x000e;
+const uint32_t image_sym_class_enum_tag = 0x000f;
+const uint32_t image_sym_class_member_of_enum = 0x0010;
+const uint32_t image_sym_class_register_param = 0x0011;
+const uint32_t image_sym_class_bit_field = 0x0012;
+
+const uint32_t image_sym_class_far_external = 0x0044;
+
+const uint32_t image_sym_class_block = 0x0064;
+const uint32_t image_sym_class_function = 0x0065;
+const uint32_t image_sym_class_end_of_struct = 0x0066;
+const uint32_t image_sym_class_file = 0x0067;
+
+const uint32_t image_sym_class_section = 0x0068;
+const uint32_t image_sym_class_weak_external = 0x0069;
+
+const uint32_t image_sym_class_clr_token = 0x006b;
+
+//type packing constants
+const uint32_t n_btmask = 0x000f;
+const uint32_t n_tmask = 0x0030;
+const uint32_t n_tmask1 = 0x00c0;
+const uint32_t n_tmask2 = 0x00f0;
+const uint32_t n_btshft = 4;
+const uint32_t n_tshift = 2;
+
+//Type (derived) values.
+const uint32_t image_sym_dtype_null = 0; // no derived type.
+const uint32_t image_sym_dtype_pointer = 1; // pointer.
+const uint32_t image_sym_dtype_function = 2; // function.
+const uint32_t image_sym_dtype_array = 3; // array.
+
+// Is x a function?
+//TODO
+#ifndef ISFCN
+#define ISFCN(x) (((x) & n_tmask) == (image_sym_dtype_function << n_btshft))
+#endif
+
+//Version info
+const uint32_t vs_ffi_fileflagsmask = 0x0000003FL;
+
+const uint32_t vs_ffi_signature = 0xFEEF04BDL;
+const uint32_t vs_ffi_strucversion = 0x00010000L;
+
+/* ----- VS_VERSION.dwFileFlags ----- */
+const uint32_t vs_ff_debug = 0x00000001L;
+const uint32_t vs_ff_prerelease = 0x00000002L;
+const uint32_t vs_ff_patched = 0x00000004L;
+const uint32_t vs_ff_privatebuild = 0x00000008L;
+const uint32_t vs_ff_infoinferred = 0x00000010L;
+const uint32_t vs_ff_specialbuild = 0x00000020L;
+
+/* ----- VS_VERSION.dwFileOS ----- */
+const uint32_t vos_unknown = 0x00000000L;
+const uint32_t vos_dos = 0x00010000L;
+const uint32_t vos_os216 = 0x00020000L;
+const uint32_t vos_os232 = 0x00030000L;
+const uint32_t vos_nt = 0x00040000L;
+const uint32_t vos_wince = 0x00050000L;
+
+const uint32_t vos__base = 0x00000000L;
+const uint32_t vos__windows16 = 0x00000001L;
+const uint32_t vos__pm16 = 0x00000002L;
+const uint32_t vos__pm32 = 0x00000003L;
+const uint32_t vos__windows32 = 0x00000004L;
+
+const uint32_t vos_dos_windows16 = 0x00010001L;
+const uint32_t vos_dos_windows32 = 0x00010004L;
+const uint32_t vos_os216_pm16 = 0x00020002L;
+const uint32_t vos_os232_pm32 = 0x00030003L;
+const uint32_t vos_nt_windows32 = 0x00040004L;
+
+/* ----- VS_VERSION.dwFileType ----- */
+const uint32_t vft_unknown = 0x00000000L;
+const uint32_t vft_app = 0x00000001L;
+const uint32_t vft_dll = 0x00000002L;
+const uint32_t vft_drv = 0x00000003L;
+const uint32_t vft_font = 0x00000004L;
+const uint32_t vft_vxd = 0x00000005L;
+const uint32_t vft_static_lib = 0x00000007L;
+
+const uint32_t message_resource_unicode = 0x0001;
+
+#pragma pack(push, 1)
+
+//Windows GUID structure
+struct guid
+{
+ uint32_t Data1;
+ uint16_t Data2;
+ uint16_t Data3;
+ uint8_t Data4[8];
+};
+
+//DOS .EXE header
+struct image_dos_header
+{
+ uint16_t e_magic; // Magic number
+ uint16_t e_cblp; // Bytes on last page of file
+ uint16_t e_cp; // Pages in file
+ uint16_t e_crlc; // Relocations
+ uint16_t e_cparhdr; // Size of header in paragraphs
+ uint16_t e_minalloc; // Minimum extra paragraphs needed
+ uint16_t e_maxalloc; // Maximum extra paragraphs needed
+ uint16_t e_ss; // Initial (relative) SS value
+ uint16_t e_sp; // Initial SP value
+ uint16_t e_csum; // Checksum
+ uint16_t e_ip; // Initial IP value
+ uint16_t e_cs; // Initial (relative) CS value
+ uint16_t e_lfarlc; // File address of relocation table
+ uint16_t e_ovno; // Overlay number
+ uint16_t e_res[4]; // Reserved words
+ uint16_t e_oemid; // OEM identifier (for e_oeminfo)
+ uint16_t e_oeminfo; // OEM information; e_oemid specific
+ uint16_t e_res2[10]; // Reserved words
+ int32_t e_lfanew; // File address of new exe header
+};
+
+//Directory format
+struct image_data_directory
+{
+ uint32_t VirtualAddress;
+ uint32_t Size;
+};
+
+//Optional header format
+struct image_optional_header32
+{
+ //Standard fields
+ uint16_t Magic;
+ uint8_t MajorLinkerVersion;
+ uint8_t MinorLinkerVersion;
+ uint32_t SizeOfCode;
+ uint32_t SizeOfInitializedData;
+ uint32_t SizeOfUninitializedData;
+ uint32_t AddressOfEntryPoint;
+ uint32_t BaseOfCode;
+ uint32_t BaseOfData;
+
+ //NT additional fields
+ uint32_t ImageBase;
+ uint32_t SectionAlignment;
+ uint32_t FileAlignment;
+ uint16_t MajorOperatingSystemVersion;
+ uint16_t MinorOperatingSystemVersion;
+ uint16_t MajorImageVersion;
+ uint16_t MinorImageVersion;
+ uint16_t MajorSubsystemVersion;
+ uint16_t MinorSubsystemVersion;
+ uint32_t Win32VersionValue;
+ uint32_t SizeOfImage;
+ uint32_t SizeOfHeaders;
+ uint32_t CheckSum;
+ uint16_t Subsystem;
+ uint16_t DllCharacteristics;
+ uint32_t SizeOfStackReserve;
+ uint32_t SizeOfStackCommit;
+ uint32_t SizeOfHeapReserve;
+ uint32_t SizeOfHeapCommit;
+ uint32_t LoaderFlags;
+ uint32_t NumberOfRvaAndSizes;
+ image_data_directory DataDirectory[image_numberof_directory_entries];
+};
+
+struct image_optional_header64
+{
+ uint16_t Magic;
+ uint8_t MajorLinkerVersion;
+ uint8_t MinorLinkerVersion;
+ uint32_t SizeOfCode;
+ uint32_t SizeOfInitializedData;
+ uint32_t SizeOfUninitializedData;
+ uint32_t AddressOfEntryPoint;
+ uint32_t BaseOfCode;
+ uint64_t ImageBase;
+ uint32_t SectionAlignment;
+ uint32_t FileAlignment;
+ uint16_t MajorOperatingSystemVersion;
+ uint16_t MinorOperatingSystemVersion;
+ uint16_t MajorImageVersion;
+ uint16_t MinorImageVersion;
+ uint16_t MajorSubsystemVersion;
+ uint16_t MinorSubsystemVersion;
+ uint32_t Win32VersionValue;
+ uint32_t SizeOfImage;
+ uint32_t SizeOfHeaders;
+ uint32_t CheckSum;
+ uint16_t Subsystem;
+ uint16_t DllCharacteristics;
+ uint64_t SizeOfStackReserve;
+ uint64_t SizeOfStackCommit;
+ uint64_t SizeOfHeapReserve;
+ uint64_t SizeOfHeapCommit;
+ uint32_t LoaderFlags;
+ uint32_t NumberOfRvaAndSizes;
+ image_data_directory DataDirectory[image_numberof_directory_entries];
+};
+
+struct image_file_header
+{
+ uint16_t Machine;
+ uint16_t NumberOfSections;
+ uint32_t TimeDateStamp;
+ uint32_t PointerToSymbolTable;
+ uint32_t NumberOfSymbols;
+ uint16_t SizeOfOptionalHeader;
+ uint16_t Characteristics;
+};
+
+struct image_nt_headers64
+{
+ uint32_t Signature;
+ image_file_header FileHeader;
+ image_optional_header64 OptionalHeader;
+};
+
+struct image_nt_headers32
+{
+ uint32_t Signature;
+ image_file_header FileHeader;
+ image_optional_header32 OptionalHeader;
+};
+
+//Section header format
+struct image_section_header
+{
+ uint8_t Name[8];
+ union
+ {
+ uint32_t PhysicalAddress;
+ uint32_t VirtualSize;
+ } Misc;
+
+ uint32_t VirtualAddress;
+ uint32_t SizeOfRawData;
+ uint32_t PointerToRawData;
+ uint32_t PointerToRelocations;
+ uint32_t PointerToLinenumbers;
+ uint16_t NumberOfRelocations;
+ uint16_t NumberOfLinenumbers;
+ uint32_t Characteristics;
+};
+
+
+/// RESOURCES ///
+struct image_resource_directory
+{
+ uint32_t Characteristics;
+ uint32_t TimeDateStamp;
+ uint16_t MajorVersion;
+ uint16_t MinorVersion;
+ uint16_t NumberOfNamedEntries;
+ uint16_t NumberOfIdEntries;
+ // IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
+};
+
+struct vs_fixedfileinfo
+{
+ uint32_t dwSignature; /* e.g. 0xfeef04bd */
+ uint32_t dwStrucVersion; /* e.g. 0x00000042 = "0.42" */
+ uint32_t dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */
+ uint32_t dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */
+ uint32_t dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */
+ uint32_t dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */
+ uint32_t dwFileFlagsMask; /* = 0x3F for version "0.42" */
+ uint32_t dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */
+ uint32_t dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */
+ uint32_t dwFileType; /* e.g. VFT_DRIVER */
+ uint32_t dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */
+ uint32_t dwFileDateMS; /* e.g. 0 */
+ uint32_t dwFileDateLS; /* e.g. 0 */
+};
+
+struct bitmapinfoheader
+{
+ uint32_t biSize;
+ int32_t biWidth;
+ int32_t biHeight;
+ uint16_t biPlanes;
+ uint16_t biBitCount;
+ uint32_t biCompression;
+ uint32_t biSizeImage;
+ int32_t biXPelsPerMeter;
+ int32_t biYPelsPerMeter;
+ uint32_t biClrUsed;
+ uint32_t biClrImportant;
+};
+
+struct message_resource_entry
+{
+ uint16_t Length;
+ uint16_t Flags;
+ uint8_t Text[1];
+};
+
+struct message_resource_block
+{
+ uint32_t LowId;
+ uint32_t HighId;
+ uint32_t OffsetToEntries;
+};
+
+struct message_resource_data
+{
+ uint32_t NumberOfBlocks;
+ message_resource_block Blocks[1];
+};
+
+struct image_resource_directory_entry
+{
+ union
+ {
+ struct
+ {
+ uint32_t NameOffset:31;
+ uint32_t NameIsString:1;
+ };
+ uint32_t Name;
+ uint16_t Id;
+ };
+
+ union
+ {
+ uint32_t OffsetToData;
+ struct
+ {
+ uint32_t OffsetToDirectory:31;
+ uint32_t DataIsDirectory:1;
+ };
+ };
+};
+
+struct image_resource_data_entry
+{
+ uint32_t OffsetToData;
+ uint32_t Size;
+ uint32_t CodePage;
+ uint32_t Reserved;
+};
+
+#pragma pack(push, 2)
+struct bitmapfileheader
+{
+ uint16_t bfType;
+ uint32_t bfSize;
+ uint16_t bfReserved1;
+ uint16_t bfReserved2;
+ uint32_t bfOffBits;
+};
+#pragma pack(pop)
+
+
+
+//Structure representing ICON file header
+struct ico_header
+{
+ uint16_t Reserved;
+ uint16_t Type; //1
+ uint16_t Count; //Count of icons included in icon group
+};
+
+//Structure that is stored in icon group directory in PE resources
+struct icon_group
+{
+ uint8_t Width;
+ uint8_t Height;
+ uint8_t ColorCount;
+ uint8_t Reserved;
+ uint16_t Planes;
+ uint16_t BitCount;
+ uint32_t SizeInBytes;
+ uint16_t Number; //Represents resource ID in PE icon list
+};
+
+//Structure representing ICON directory entry inside ICON file
+struct icondirentry
+{
+ uint8_t Width;
+ uint8_t Height;
+ uint8_t ColorCount;
+ uint8_t Reserved;
+ uint16_t Planes;
+ uint16_t BitCount;
+ uint32_t SizeInBytes;
+ uint32_t ImageOffset; //Offset from start of header to the image
+};
+
+//Structure representing CURSOR file header
+struct cursor_header
+{
+ uint16_t Reserved;
+ uint16_t Type; //2
+ uint16_t Count; //Count of cursors included in cursor group
+};
+
+struct cursor_group
+{
+ uint16_t Width;
+ uint16_t Height; //Divide by 2 to get the actual height.
+ uint16_t Planes;
+ uint16_t BitCount;
+ uint32_t SizeInBytes;
+ uint16_t Number; //Represents resource ID in PE icon list
+};
+
+//Structure representing CURSOR directory entry inside CURSOR file
+struct cursordirentry
+{
+ uint8_t Width; //Set to CURSOR_GROUP::Height/2.
+ uint8_t Height;
+ uint8_t ColorCount;
+ uint8_t Reserved;
+ uint16_t HotspotX;
+ uint16_t HotspotY;
+ uint32_t SizeInBytes;
+ uint32_t ImageOffset; //Offset from start of header to the image
+};
+
+//Structure representing BLOCK in version info resource
+struct version_info_block //(always aligned on 32-bit (DWORD) boundary)
+{
+ uint16_t Length; //Length of this block (doesn't include padding)
+ uint16_t ValueLength; //Value length (if any)
+ uint16_t Type; //Value type (0 = binary, 1 = text)
+ uint16_t Key[1]; //Value name (block key) (always NULL terminated)
+
+ //////////
+ //WORD padding1[]; //Padding, if any (ALIGNMENT)
+ //xxxxx Value[]; //Value data, if any (*ALIGNED*)
+ //WORD padding2[]; //Padding, if any (ALIGNMENT)
+ //xxxxx Child[]; //Child block(s), if any (*ALIGNED*)
+ //////////
+};
+
+
+/// IMPORTS ///
+#pragma pack(push, 8)
+struct image_thunk_data64
+{
+ union
+ {
+ uint64_t ForwarderString; // PBYTE
+ uint64_t Function; // PDWORD
+ uint64_t Ordinal;
+ uint64_t AddressOfData; // PIMAGE_IMPORT_BY_NAME
+ } u1;
+};
+#pragma pack(pop)
+
+struct image_thunk_data32
+{
+ union
+ {
+ uint32_t ForwarderString; // PBYTE
+ uint32_t Function; // PDWORD
+ uint32_t Ordinal;
+ uint32_t AddressOfData; // PIMAGE_IMPORT_BY_NAME
+ } u1;
+};
+
+struct image_import_descriptor
+{
+ union
+ {
+ uint32_t Characteristics; // 0 for terminating null import descriptor
+ uint32_t OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
+ };
+
+ uint32_t TimeDateStamp; // 0 if not bound,
+ // -1 if bound, and real date\time stamp
+ // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
+ // O.W. date/time stamp of DLL bound to (Old BIND)
+
+ uint32_t ForwarderChain; // -1 if no forwarders
+ uint32_t Name;
+ uint32_t FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
+};
+
+
+/// TLS ///
+struct image_tls_directory64
+{
+ uint64_t StartAddressOfRawData;
+ uint64_t EndAddressOfRawData;
+ uint64_t AddressOfIndex; // PDWORD
+ uint64_t AddressOfCallBacks; // PIMAGE_TLS_CALLBACK *;
+ uint32_t SizeOfZeroFill;
+ uint32_t Characteristics;
+};
+
+struct image_tls_directory32
+{
+ uint32_t StartAddressOfRawData;
+ uint32_t EndAddressOfRawData;
+ uint32_t AddressOfIndex; // PDWORD
+ uint32_t AddressOfCallBacks; // PIMAGE_TLS_CALLBACK *
+ uint32_t SizeOfZeroFill;
+ uint32_t Characteristics;
+};
+
+
+/// Export Format ///
+struct image_export_directory
+{
+ uint32_t Characteristics;
+ uint32_t TimeDateStamp;
+ uint16_t MajorVersion;
+ uint16_t MinorVersion;
+ uint32_t Name;
+ uint32_t Base;
+ uint32_t NumberOfFunctions;
+ uint32_t NumberOfNames;
+ uint32_t AddressOfFunctions; // RVA from base of image
+ uint32_t AddressOfNames; // RVA from base of image
+ uint32_t AddressOfNameOrdinals; // RVA from base of image
+};
+
+
+/// Based relocation format ///
+struct image_base_relocation
+{
+ uint32_t VirtualAddress;
+ uint32_t SizeOfBlock;
+ // uint16_t TypeOffset[1];
+};
+
+
+/// New format import descriptors pointed to by DataDirectory[ IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ] ///
+struct image_bound_import_descriptor
+{
+ uint32_t TimeDateStamp;
+ uint16_t OffsetModuleName;
+ uint16_t NumberOfModuleForwarderRefs;
+ // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows
+};
+
+struct image_bound_forwarder_ref
+{
+ uint32_t TimeDateStamp;
+ uint16_t OffsetModuleName;
+ uint16_t Reserved;
+};
+
+
+/// Exception directory ///
+struct image_runtime_function_entry
+{
+ uint32_t BeginAddress;
+ uint32_t EndAddress;
+ uint32_t UnwindInfoAddress;
+};
+
+enum unwind_op_codes
+{
+ uwop_push_nonvol = 0, /* info == register number */
+ uwop_alloc_large, /* no info, alloc size in next 2 slots */
+ uwop_alloc_small, /* info == size of allocation / 8 - 1 */
+ uwop_set_fpreg, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
+ uwop_save_nonvol, /* info == register number, offset in next slot */
+ uwop_save_nonvol_far, /* info == register number, offset in next 2 slots */
+ uwop_save_xmm128, /* info == XMM reg number, offset in next slot */
+ uwop_save_xmm128_far, /* info == XMM reg number, offset in next 2 slots */
+ uwop_push_machframe /* info == 0: no error-code, 1: error-code */
+};
+
+union unwind_code
+{
+ struct s
+ {
+ uint8_t CodeOffset;
+ uint8_t UnwindOp : 4;
+ uint8_t OpInfo : 4;
+ };
+
+ uint16_t FrameOffset;
+};
+
+struct unwind_info
+{
+ uint8_t Version : 3;
+ uint8_t Flags : 5;
+ uint8_t SizeOfProlog;
+ uint8_t CountOfCodes;
+ uint8_t FrameRegister : 4;
+ uint8_t FrameOffset : 4;
+ unwind_code UnwindCode[1];
+ /* unwind_code MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
+ * union {
+ * OPTIONAL ULONG ExceptionHandler;
+ * OPTIONAL ULONG FunctionEntry;
+ * };
+ * OPTIONAL ULONG ExceptionData[]; */
+};
+
+
+
+/// Debug ///
+struct image_debug_misc
+{
+ uint32_t DataType; // type of misc data, see defines
+ uint32_t Length; // total length of record, rounded to four
+ // byte multiple.
+ uint8_t Unicode; // TRUE if data is unicode string
+ uint8_t Reserved[3];
+ uint8_t Data[1]; // Actual data
+};
+
+struct image_coff_symbols_header
+{
+ uint32_t NumberOfSymbols;
+ uint32_t LvaToFirstSymbol;
+ uint32_t NumberOfLinenumbers;
+ uint32_t LvaToFirstLinenumber;
+ uint32_t RvaToFirstByteOfCode;
+ uint32_t RvaToLastByteOfCode;
+ uint32_t RvaToFirstByteOfData;
+ uint32_t RvaToLastByteOfData;
+};
+
+struct image_debug_directory
+{
+ uint32_t Characteristics;
+ uint32_t TimeDateStamp;
+ uint16_t MajorVersion;
+ uint16_t MinorVersion;
+ uint32_t Type;
+ uint32_t SizeOfData;
+ uint32_t AddressOfRawData;
+ uint32_t PointerToRawData;
+};
+
+
+#pragma pack(push, 2)
+struct image_symbol
+{
+ union
+ {
+ uint8_t ShortName[8];
+ struct
+ {
+ uint32_t Short; // if 0, use LongName
+ uint32_t Long; // offset into string table
+ } Name;
+ uint32_t LongName[2]; // PBYTE [2]
+ } N;
+ uint32_t Value;
+ int16_t SectionNumber;
+ uint16_t Type;
+ uint8_t StorageClass;
+ uint8_t NumberOfAuxSymbols;
+};
+#pragma pack(pop)
+
+//CodeView Debug OMF signature. The signature at the end of the file is
+//a negative offset from the end of the file to another signature. At
+//the negative offset (base address) is another signature whose filepos
+//field points to the first OMFDirHeader in a chain of directories.
+//The NB05 signature is used by the link utility to indicated a completely
+//unpacked file. The NB06 signature is used by ilink to indicate that the
+//executable has had CodeView information from an incremental link appended
+//to the executable. The NB08 signature is used by cvpack to indicate that
+//the CodeView Debug OMF has been packed. CodeView will only process
+//executables with the NB08 signature.
+struct OMFSignature
+{
+ char Signature[4]; // "NBxx"
+ uint32_t filepos; // offset in file
+};
+
+struct CV_INFO_PDB20
+{
+ OMFSignature CvHeader;
+ uint32_t Signature;
+ uint32_t Age;
+ uint8_t PdbFileName[1];
+};
+
+struct CV_INFO_PDB70
+{
+ uint32_t CvSignature;
+ guid Signature;
+ uint32_t Age;
+ uint8_t PdbFileName[1];
+};
+
+// directory information structure
+// This structure contains the information describing the directory.
+// It is pointed to by the signature at the base address or the directory
+// link field of a preceeding directory. The directory entries immediately
+// follow this structure.
+struct OMFDirHeader
+{
+ uint16_t cbDirHeader; // length of this structure
+ uint16_t cbDirEntry; // number of bytes in each directory entry
+ uint32_t cDir; // number of directorie entries
+ int32_t lfoNextDir; // offset from base of next directory
+ uint32_t flags; // status flags
+};
+
+// directory structure
+// The data in this structure is used to reference the data for each
+// subsection of the CodeView Debug OMF information. Tables that are
+// not associated with a specific module will have a module index of
+// oxffff. These tables are the global types table, the global symbol
+// table, the global public table and the library table.
+struct OMFDirEntry
+{
+ uint16_t SubSection; // subsection type (sst...)
+ uint16_t iMod; // module index
+ int32_t lfo; // large file offset of subsection
+ uint32_t cb; // number of bytes in subsection
+};
+
+
+/// CLR 2.0 header structure ///
+struct image_cor20_header
+{
+ //Header versioning
+ uint32_t cb;
+ uint16_t MajorRuntimeVersion;
+ uint16_t MinorRuntimeVersion;
+
+ // Symbol table and startup information
+ image_data_directory MetaData;
+ uint32_t Flags;
+
+ // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is not set, EntryPointToken represents a managed entrypoint.
+ // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is set, EntryPointRVA represents an RVA to a native entrypoint.
+ union
+ {
+ uint32_t EntryPointToken;
+ uint32_t EntryPointRVA;
+ };
+
+ // Binding information
+ image_data_directory Resources;
+ image_data_directory StrongNameSignature;
+
+ // Regular fixup and binding information
+ image_data_directory CodeManagerTable;
+ image_data_directory VTableFixups;
+ image_data_directory ExportAddressTableJumps;
+
+ // Precompiled image info (internal use only - set to zero)
+ image_data_directory ManagedNativeHeader;
+};
+
+enum replaces_cor_hdr_numeric_defines
+{
+ // COM+ Header entry point flags.
+ comimage_flags_ilonly =0x00000001,
+ comimage_flags_32bitrequired =0x00000002,
+ comimage_flags_il_library =0x00000004,
+ comimage_flags_strongnamesigned =0x00000008,
+ comimage_flags_native_entrypoint =0x00000010,
+ comimage_flags_trackdebugdata =0x00010000,
+
+ // Version flags for image.
+ cor_version_major_v2 =2,
+ cor_version_major =cor_version_major_v2,
+ cor_version_minor =0,
+ cor_deleted_name_length =8,
+ cor_vtablegap_name_length =8,
+
+ // Maximum size of a NativeType descriptor.
+ native_type_max_cb =1,
+ cor_ilmethod_sect_small_max_datasize=0xff,
+
+ // #defines for the MIH FLAGS
+ image_cor_mih_methodrva =0x01,
+ image_cor_mih_ehrva =0x02,
+ image_cor_mih_basicblock =0x08,
+
+ // V-table constants
+ cor_vtable_32bit =0x01, // V-table slots are 32-bits in size.
+ cor_vtable_64bit =0x02, // V-table slots are 64-bits in size.
+ cor_vtable_from_unmanaged =0x04, // If set, transition from unmanaged.
+ cor_vtable_from_unmanaged_retain_appdomain =0x08, // If set, transition from unmanaged with keeping the current appdomain.
+ cor_vtable_call_most_derived =0x10, // Call most derived method described by
+
+ // EATJ constants
+ image_cor_eatj_thunk_size =32, // Size of a jump thunk reserved range.
+
+ // Max name lengths
+ //@todo: Change to unlimited name lengths.
+ max_class_name =1024,
+ max_package_name =1024
+};
+
+/// Load Configuration Directory Entry ///
+struct image_load_config_directory32
+{
+ uint32_t Size;
+ uint32_t TimeDateStamp;
+ uint16_t MajorVersion;
+ uint16_t MinorVersion;
+ uint32_t GlobalFlagsClear;
+ uint32_t GlobalFlagsSet;
+ uint32_t CriticalSectionDefaultTimeout;
+ uint32_t DeCommitFreeBlockThreshold;
+ uint32_t DeCommitTotalFreeThreshold;
+ uint32_t LockPrefixTable; // VA
+ uint32_t MaximumAllocationSize;
+ uint32_t VirtualMemoryThreshold;
+ uint32_t ProcessHeapFlags;
+ uint32_t ProcessAffinityMask;
+ uint16_t CSDVersion;
+ uint16_t Reserved1;
+ uint32_t EditList; // VA
+ uint32_t SecurityCookie; // VA
+ uint32_t SEHandlerTable; // VA
+ uint32_t SEHandlerCount;
+};
+
+struct image_load_config_directory64
+{
+ uint32_t Size;
+ uint32_t TimeDateStamp;
+ uint16_t MajorVersion;
+ uint16_t MinorVersion;
+ uint32_t GlobalFlagsClear;
+ uint32_t GlobalFlagsSet;
+ uint32_t CriticalSectionDefaultTimeout;
+ uint64_t DeCommitFreeBlockThreshold;
+ uint64_t DeCommitTotalFreeThreshold;
+ uint64_t LockPrefixTable; // VA
+ uint64_t MaximumAllocationSize;
+ uint64_t VirtualMemoryThreshold;
+ uint64_t ProcessAffinityMask;
+ uint32_t ProcessHeapFlags;
+ uint16_t CSDVersion;
+ uint16_t Reserved1;
+ uint64_t EditList; // VA
+ uint64_t SecurityCookie; // VA
+ uint64_t SEHandlerTable; // VA
+ uint64_t SEHandlerCount;
+};
+
+#pragma pack(pop)
+} //namespace pe_win
+
+#ifdef PE_BLISS_WINDOWS
+typedef wchar_t unicode16_t;
+typedef std::basic_string<unicode16_t> u16string;
+#else
+//Instead of wchar_t for windows
+typedef unsigned short unicode16_t;
+typedef std::basic_string<unicode16_t> u16string;
+#endif
+
+} //namespace pe_bliss
diff --git a/tools/pe_bliss/pe_tls.cpp b/tools/pe_bliss/pe_tls.cpp
new file mode 100644
index 0000000000..5ec68e3f10
--- /dev/null
+++ b/tools/pe_bliss/pe_tls.cpp
@@ -0,0 +1,396 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <string.h>
+#include "pe_tls.h"
+#include "pe_properties_generic.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//TLS
+//Default constructor
+tls_info::tls_info()
+ :start_rva_(0), end_rva_(0), index_rva_(0), callbacks_rva_(0),
+ size_of_zero_fill_(0), characteristics_(0)
+{}
+
+//Returns start RVA of TLS raw data
+uint32_t tls_info::get_raw_data_start_rva() const
+{
+ return start_rva_;
+}
+
+//Returns end RVA of TLS raw data
+uint32_t tls_info::get_raw_data_end_rva() const
+{
+ return end_rva_;
+}
+
+//Returns TLS index RVA
+uint32_t tls_info::get_index_rva() const
+{
+ return index_rva_;
+}
+
+//Returns TLS callbacks RVA
+uint32_t tls_info::get_callbacks_rva() const
+{
+ return callbacks_rva_;
+}
+
+//Returns size of zero fill
+uint32_t tls_info::get_size_of_zero_fill() const
+{
+ return size_of_zero_fill_;
+}
+
+//Returns characteristics
+uint32_t tls_info::get_characteristics() const
+{
+ return characteristics_;
+}
+
+//Returns raw TLS data
+const std::string& tls_info::get_raw_data() const
+{
+ return raw_data_;
+}
+
+//Returns TLS callbacks addresses
+const tls_info::tls_callback_list& tls_info::get_tls_callbacks() const
+{
+ return callbacks_;
+}
+
+//Returns TLS callbacks addresses
+tls_info::tls_callback_list& tls_info::get_tls_callbacks()
+{
+ return callbacks_;
+}
+
+//Adds TLS callback
+void tls_info::add_tls_callback(uint32_t rva)
+{
+ callbacks_.push_back(rva);
+}
+
+//Clears TLS callbacks list
+void tls_info::clear_tls_callbacks()
+{
+ callbacks_.clear();
+}
+
+//Recalculates end address of raw TLS data
+void tls_info::recalc_raw_data_end_rva()
+{
+ end_rva_ = static_cast<uint32_t>(start_rva_ + raw_data_.length());
+}
+
+//Sets start RVA of TLS raw data
+void tls_info::set_raw_data_start_rva(uint32_t rva)
+{
+ start_rva_ = rva;
+}
+
+//Sets end RVA of TLS raw data
+void tls_info::set_raw_data_end_rva(uint32_t rva)
+{
+ end_rva_ = rva;
+}
+
+//Sets TLS index RVA
+void tls_info::set_index_rva(uint32_t rva)
+{
+ index_rva_ = rva;
+}
+
+//Sets TLS callbacks RVA
+void tls_info::set_callbacks_rva(uint32_t rva)
+{
+ callbacks_rva_ = rva;
+}
+
+//Sets size of zero fill
+void tls_info::set_size_of_zero_fill(uint32_t size)
+{
+ size_of_zero_fill_ = size;
+}
+
+//Sets characteristics
+void tls_info::set_characteristics(uint32_t characteristics)
+{
+ characteristics_ = characteristics;
+}
+
+//Sets raw TLS data
+void tls_info::set_raw_data(const std::string& data)
+{
+ raw_data_ = data;
+}
+
+//If image does not have TLS, throws an exception
+const tls_info get_tls_info(const pe_base& pe)
+{
+ return pe.get_pe_type() == pe_type_32
+ ? get_tls_info_base<pe_types_class_32>(pe)
+ : get_tls_info_base<pe_types_class_64>(pe);
+}
+
+//TLS Rebuilder
+const image_directory rebuild_tls(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start, bool write_tls_callbacks, bool write_tls_data, tls_data_expand_type expand, bool save_to_pe_header, bool auto_strip_last_section)
+{
+ return pe.get_pe_type() == pe_type_32
+ ? rebuild_tls_base<pe_types_class_32>(pe, info, tls_section, offset_from_section_start, write_tls_callbacks, write_tls_data, expand, save_to_pe_header, auto_strip_last_section)
+ : rebuild_tls_base<pe_types_class_64>(pe, info, tls_section, offset_from_section_start, write_tls_callbacks, write_tls_data, expand, save_to_pe_header, auto_strip_last_section);
+}
+
+//Get TLS info
+//If image does not have TLS, throws an exception
+template<typename PEClassType>
+const tls_info get_tls_info_base(const pe_base& pe)
+{
+ tls_info ret;
+
+ //If there's no TLS directory, throw an exception
+ if(!pe.has_tls())
+ throw pe_exception("Image does not have TLS directory", pe_exception::directory_does_not_exist);
+
+ //Get TLS directory data
+ typename PEClassType::TLSStruct tls_directory_data = pe.section_data_from_rva<typename PEClassType::TLSStruct>(pe.get_directory_rva(image_directory_entry_tls), section_data_virtual, true);
+
+ //Check data addresses
+ if(tls_directory_data.EndAddressOfRawData == tls_directory_data.StartAddressOfRawData)
+ {
+ try
+ {
+ pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.EndAddressOfRawData));
+ }
+ catch(const pe_exception&)
+ {
+ //Fix addressess on incorrect conversion
+ tls_directory_data.EndAddressOfRawData = tls_directory_data.StartAddressOfRawData = 0;
+ }
+ }
+
+ if(tls_directory_data.StartAddressOfRawData &&
+ pe.section_data_length_from_va(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData),
+ static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), section_data_virtual, true)
+ < (tls_directory_data.EndAddressOfRawData - tls_directory_data.StartAddressOfRawData))
+ throw pe_exception("Incorrect TLS directory", pe_exception::incorrect_tls_directory);
+
+ //Fill TLS info
+ //VAs are not checked
+ ret.set_raw_data_start_rva(tls_directory_data.StartAddressOfRawData ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData)) : 0);
+ ret.set_raw_data_end_rva(tls_directory_data.EndAddressOfRawData ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.EndAddressOfRawData)) : 0);
+ ret.set_index_rva(tls_directory_data.AddressOfIndex ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfIndex)) : 0);
+ ret.set_callbacks_rva(tls_directory_data.AddressOfCallBacks ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfCallBacks)) : 0);
+ ret.set_size_of_zero_fill(tls_directory_data.SizeOfZeroFill);
+ ret.set_characteristics(tls_directory_data.Characteristics);
+
+ if(tls_directory_data.StartAddressOfRawData && tls_directory_data.StartAddressOfRawData != tls_directory_data.EndAddressOfRawData)
+ {
+ //Read and save TLS RAW data
+ ret.set_raw_data(std::string(
+ pe.section_data_from_va(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), section_data_virtual, true),
+ static_cast<uint32_t>(tls_directory_data.EndAddressOfRawData - tls_directory_data.StartAddressOfRawData)));
+ }
+
+ //If file has TLS callbacks
+ if(ret.get_callbacks_rva())
+ {
+ //Read callbacks VAs
+ uint32_t current_tls_callback = 0;
+
+ while(true)
+ {
+ //Read TLS callback VA
+ typename PEClassType::BaseSize va = pe.section_data_from_va<typename PEClassType::BaseSize>(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfCallBacks + current_tls_callback), section_data_virtual, true);
+ if(va == 0)
+ break;
+
+ //Save it
+ ret.add_tls_callback(pe.va_to_rva(va, false));
+
+ //Move to next callback VA
+ current_tls_callback += sizeof(va);
+ }
+ }
+
+ return ret;
+}
+
+//Rebuilder of TLS structures
+//If write_tls_callbacks = true, TLS callbacks VAs will be written to their place
+//If write_tls_data = true, TLS data will be written to its place
+//If you have chosen to rewrite raw data, only (EndAddressOfRawData - StartAddressOfRawData) bytes will be written, not the full length of string
+//representing raw data content
+//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped
+//Note/TODO: TLS Callbacks array is not DWORD-aligned (seems to work on WinXP - Win7)
+template<typename PEClassType>
+const image_directory rebuild_tls_base(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start, bool write_tls_callbacks, bool write_tls_data, tls_data_expand_type expand, bool save_to_pe_header, bool auto_strip_last_section)
+{
+ //Check that tls_section is attached to this PE image
+ if(!pe.section_attached(tls_section))
+ throw pe_exception("TLS section must be attached to PE file", pe_exception::section_is_not_attached);
+
+ uint32_t tls_data_pos = pe_utils::align_up(offset_from_section_start, sizeof(typename PEClassType::BaseSize));
+ uint32_t needed_size = sizeof(typename PEClassType::TLSStruct); //Calculate needed size for TLS table
+
+ //Check if tls_section is last one. If it's not, check if there's enough place for TLS data
+ if(&tls_section != &*(pe.get_image_sections().end() - 1) &&
+ (tls_section.empty() || pe_utils::align_up(tls_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + tls_data_pos))
+ throw pe_exception("Insufficient space for TLS directory", pe_exception::insufficient_space);
+
+ //Check raw data positions
+ if(info.get_raw_data_end_rva() < info.get_raw_data_start_rva() || info.get_index_rva() == 0)
+ throw pe_exception("Incorrect TLS directory", pe_exception::incorrect_tls_directory);
+
+ std::string& raw_data = tls_section.get_raw_data();
+
+ //This will be done only if tls_section is the last section of image or for section with unaligned raw length of data
+ if(raw_data.length() < needed_size + tls_data_pos)
+ raw_data.resize(needed_size + tls_data_pos); //Expand section raw data
+
+ //Create and fill TLS structure
+ typename PEClassType::TLSStruct tls_struct = {0};
+
+ typename PEClassType::BaseSize va;
+ if(info.get_raw_data_start_rva())
+ {
+ pe.rva_to_va(info.get_raw_data_start_rva(), va);
+ tls_struct.StartAddressOfRawData = va;
+ tls_struct.SizeOfZeroFill = info.get_size_of_zero_fill();
+ }
+
+ if(info.get_raw_data_end_rva())
+ {
+ pe.rva_to_va(info.get_raw_data_end_rva(), va);
+ tls_struct.EndAddressOfRawData = va;
+ }
+
+ pe.rva_to_va(info.get_index_rva(), va);
+ tls_struct.AddressOfIndex = va;
+
+ if(info.get_callbacks_rva())
+ {
+ pe.rva_to_va(info.get_callbacks_rva(), va);
+ tls_struct.AddressOfCallBacks = va;
+ }
+
+ tls_struct.Characteristics = info.get_characteristics();
+
+ //Save TLS structure
+ memcpy(&raw_data[tls_data_pos], &tls_struct, sizeof(tls_struct));
+
+ //If we are asked to rewrite TLS raw data
+ if(write_tls_data && info.get_raw_data_start_rva() && info.get_raw_data_start_rva() != info.get_raw_data_end_rva())
+ {
+ try
+ {
+ //Check if we're going to write TLS raw data to an existing section (not to PE headers)
+ section& raw_data_section = pe.section_from_rva(info.get_raw_data_start_rva());
+ pe.expand_section(raw_data_section, info.get_raw_data_start_rva(), info.get_raw_data_end_rva() - info.get_raw_data_start_rva(), expand == tls_data_expand_raw ? pe_base::expand_section_raw : pe_base::expand_section_virtual);
+ }
+ catch(const pe_exception&)
+ {
+ //If no section is presented by StartAddressOfRawData, just go to next step
+ }
+
+ unsigned long write_raw_data_size = info.get_raw_data_end_rva() - info.get_raw_data_start_rva();
+ unsigned long available_raw_length = 0;
+
+ //Check if there's enough RAW space to write raw TLS data...
+ if((available_raw_length = pe.section_data_length_from_rva(info.get_raw_data_start_rva(), info.get_raw_data_start_rva(), section_data_raw, true))
+ < info.get_raw_data_end_rva() - info.get_raw_data_start_rva())
+ {
+ //Check if there's enough virtual space for it...
+ if(pe.section_data_length_from_rva(info.get_raw_data_start_rva(), info.get_raw_data_start_rva(), section_data_virtual, true)
+ < info.get_raw_data_end_rva() - info.get_raw_data_start_rva())
+ throw pe_exception("Insufficient space for TLS raw data", pe_exception::insufficient_space);
+ else
+ write_raw_data_size = available_raw_length; //We'll write just a part of full raw data
+ }
+
+ //Write raw TLS data, if any
+ if(write_raw_data_size != 0)
+ memcpy(pe.section_data_from_rva(info.get_raw_data_start_rva(), true), info.get_raw_data().data(), write_raw_data_size);
+ }
+
+ //If we are asked to rewrite TLS callbacks addresses
+ if(write_tls_callbacks && info.get_callbacks_rva())
+ {
+ unsigned long needed_callback_size = static_cast<unsigned long>((info.get_tls_callbacks().size() + 1 /* last null element */) * sizeof(typename PEClassType::BaseSize));
+
+ try
+ {
+ //Check if we're going to write TLS callbacks VAs to an existing section (not to PE headers)
+ section& raw_data_section = pe.section_from_rva(info.get_callbacks_rva());
+ pe.expand_section(raw_data_section, info.get_callbacks_rva(), needed_callback_size, pe_base::expand_section_raw);
+ }
+ catch(const pe_exception&)
+ {
+ //If no section is presented by RVA of callbacks, just go to next step
+ }
+
+ //Check if there's enough space to write callbacks TLS data...
+ if(pe.section_data_length_from_rva(info.get_callbacks_rva(), info.get_callbacks_rva(), section_data_raw, true)
+ < needed_callback_size - sizeof(typename PEClassType::BaseSize) /* last zero element can be virtual only */)
+ throw pe_exception("Insufficient space for TLS callbacks data", pe_exception::insufficient_space);
+
+ if(pe.section_data_length_from_rva(info.get_callbacks_rva(), info.get_callbacks_rva(), section_data_virtual, true)
+ < needed_callback_size /* check here full virtual data length available */)
+ throw pe_exception("Insufficient space for TLS callbacks data", pe_exception::insufficient_space);
+
+ std::vector<typename PEClassType::BaseSize> callbacks_virtual_addresses;
+ callbacks_virtual_addresses.reserve(info.get_tls_callbacks().size() + 1 /* last null element */);
+
+ //Convert TLS RVAs to VAs
+ for(tls_info::tls_callback_list::const_iterator it = info.get_tls_callbacks().begin(); it != info.get_tls_callbacks().end(); ++it)
+ {
+ typename PEClassType::BaseSize cb_va = 0;
+ pe.rva_to_va(*it, cb_va);
+ callbacks_virtual_addresses.push_back(cb_va);
+ }
+
+ //Ending null element
+ callbacks_virtual_addresses.push_back(0);
+
+ //Write callbacks TLS data
+ memcpy(pe.section_data_from_rva(info.get_callbacks_rva(), true), &callbacks_virtual_addresses[0], needed_callback_size);
+ }
+
+ //Adjust section raw and virtual sizes
+ pe.recalculate_section_sizes(tls_section, auto_strip_last_section);
+
+ image_directory ret(pe.rva_from_section_offset(tls_section, tls_data_pos), needed_size);
+
+ //If auto-rewrite of PE headers is required
+ if(save_to_pe_header)
+ {
+ pe.set_directory_rva(image_directory_entry_tls, ret.get_rva());
+ pe.set_directory_size(image_directory_entry_tls, ret.get_size());
+ }
+
+ return ret;
+}
+}
diff --git a/tools/pe_bliss/pe_tls.h b/tools/pe_bliss/pe_tls.h
new file mode 100644
index 0000000000..316e208147
--- /dev/null
+++ b/tools/pe_bliss/pe_tls.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <memory>
+#include <istream>
+#include "pe_base.h"
+#include "pe_directory.h"
+
+namespace pe_bliss
+{
+//Class representing TLS info
+//We use "DWORD" type to represent RVAs, because RVA is
+//always 32bit even in PE+
+class tls_info
+{
+public:
+ typedef std::vector<uint32_t> tls_callback_list;
+
+public:
+ //Default constructor
+ tls_info();
+
+ //Returns start RVA of TLS raw data
+ uint32_t get_raw_data_start_rva() const;
+ //Returns end RVA of TLS raw data
+ uint32_t get_raw_data_end_rva() const;
+ //Returns TLS index RVA
+ uint32_t get_index_rva() const;
+ //Returns TLS callbacks RVA
+ uint32_t get_callbacks_rva() const;
+ //Returns size of zero fill
+ uint32_t get_size_of_zero_fill() const;
+ //Returns characteristics
+ uint32_t get_characteristics() const;
+ //Returns raw TLS data
+ const std::string& get_raw_data() const;
+ //Returns TLS callbacks addresses
+ const tls_callback_list& get_tls_callbacks() const;
+
+public: //These functions do not change everything inside image, they are used by PE class
+ //You can also use them to rebuild TLS directory
+
+ //Sets start RVA of TLS raw data
+ void set_raw_data_start_rva(uint32_t rva);
+ //Sets end RVA of TLS raw data
+ void set_raw_data_end_rva(uint32_t rva);
+ //Sets TLS index RVA
+ void set_index_rva(uint32_t rva);
+ //Sets TLS callbacks RVA
+ void set_callbacks_rva(uint32_t rva);
+ //Sets size of zero fill
+ void set_size_of_zero_fill(uint32_t size);
+ //Sets characteristics
+ void set_characteristics(uint32_t characteristics);
+ //Sets raw TLS data
+ void set_raw_data(const std::string& data);
+ //Returns TLS callbacks addresses
+ tls_callback_list& get_tls_callbacks();
+ //Adds TLS callback
+ void add_tls_callback(uint32_t rva);
+ //Clears TLS callbacks list
+ void clear_tls_callbacks();
+ //Recalculates end address of raw TLS data
+ void recalc_raw_data_end_rva();
+
+private:
+ uint32_t start_rva_, end_rva_, index_rva_, callbacks_rva_;
+ uint32_t size_of_zero_fill_, characteristics_;
+
+ //Raw TLS data
+ std::string raw_data_;
+
+ //TLS callback RVAs
+ tls_callback_list callbacks_;
+};
+
+//Represents type of expanding of TLS section containing raw data
+//(Works only if you are writing TLS raw data to tls_section and it is the last one in the PE image on the moment of TLS rebuild)
+enum tls_data_expand_type
+{
+ tls_data_expand_raw, //If there is not enough raw space for raw TLS data, it can be expanded
+ tls_data_expand_virtual //If there is not enough virtual place for raw TLS data, it can be expanded
+};
+
+
+//Get TLS info
+//If image does not have TLS, throws an exception
+const tls_info get_tls_info(const pe_base& pe);
+
+template<typename PEClassType>
+const tls_info get_tls_info_base(const pe_base& pe);
+
+//Rebuilder of TLS structures
+//If write_tls_callbacks = true, TLS callbacks VAs will be written to their place
+//If write_tls_data = true, TLS data will be written to its place
+//If you have chosen to rewrite raw data, only (EndAddressOfRawData - StartAddressOfRawData) bytes will be written, not the full length of string
+//representing raw data content
+//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped
+const image_directory rebuild_tls(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start = 0, bool write_tls_callbacks = true, bool write_tls_data = true, tls_data_expand_type expand = tls_data_expand_raw, bool save_to_pe_header = true, bool auto_strip_last_section = true);
+
+template<typename PEClassType>
+const image_directory rebuild_tls_base(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start = 0, bool write_tls_callbacks = true, bool write_tls_data = true, tls_data_expand_type expand = tls_data_expand_raw, bool save_to_pe_header = true, bool auto_strip_last_section = true);
+}
diff --git a/tools/pe_bliss/resource_bitmap_reader.cpp b/tools/pe_bliss/resource_bitmap_reader.cpp
new file mode 100644
index 0000000000..3546461f53
--- /dev/null
+++ b/tools/pe_bliss/resource_bitmap_reader.cpp
@@ -0,0 +1,86 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <cmath>
+#include "resource_bitmap_reader.h"
+#include "pe_resource_viewer.h"
+#include "pe_structures.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+resource_bitmap_reader::resource_bitmap_reader(const pe_resource_viewer& res)
+ :res_(res)
+{}
+
+//Returns bitmap data by name and index in language directory (instead of language) (minimum checks of format correctness)
+const std::string resource_bitmap_reader::get_bitmap_by_name(const std::wstring& name, uint32_t index) const
+{
+ return create_bitmap(res_.get_resource_data_by_name(pe_resource_viewer::resource_bitmap, name, index).get_data());
+}
+
+//Returns bitmap data by name and language (minimum checks of format correctness)
+const std::string resource_bitmap_reader::get_bitmap_by_name(uint32_t language, const std::wstring& name) const
+{
+ return create_bitmap(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_bitmap, name).get_data());
+}
+
+//Returns bitmap data by ID and language (minimum checks of format correctness)
+const std::string resource_bitmap_reader::get_bitmap_by_id_lang(uint32_t language, uint32_t id) const
+{
+ return create_bitmap(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_bitmap, id).get_data());
+}
+
+//Returns bitmap data by ID and index in language directory (instead of language) (minimum checks of format correctness)
+const std::string resource_bitmap_reader::get_bitmap_by_id(uint32_t id, uint32_t index) const
+{
+ return create_bitmap(res_.get_resource_data_by_id(pe_resource_viewer::resource_bitmap, id, index).get_data());
+}
+
+//Helper function of creating bitmap header
+const std::string resource_bitmap_reader::create_bitmap(const std::string& resource_data)
+{
+ //Create bitmap file header
+ bitmapfileheader header = {0};
+ header.bfType = 0x4d42; //Signature "BM"
+ header.bfOffBits = sizeof(bitmapfileheader) + sizeof(bitmapinfoheader); //Offset to bitmap bits
+ header.bfSize = static_cast<uint32_t>(sizeof(bitmapfileheader) + resource_data.length()); //Size of bitmap
+
+ //Check size of resource data
+ if(resource_data.length() < sizeof(bitmapinfoheader))
+ throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap);
+
+ {
+ //Get bitmap info header
+ const bitmapinfoheader* info = reinterpret_cast<const bitmapinfoheader*>(resource_data.data());
+
+ //If color table is present, skip it
+ if(info->biClrUsed != 0)
+ header.bfOffBits += 4 * info->biClrUsed; //Add this size to offset to bitmap bits
+ else if(info->biBitCount <= 8)
+ header.bfOffBits += 4 * static_cast<uint32_t>(std::pow(2.f, info->biBitCount)); //Add this size to offset to bitmap bits
+ }
+
+ //Return final bitmap data
+ return std::string(reinterpret_cast<const char*>(&header), sizeof(bitmapfileheader)) + resource_data;
+}
+}
diff --git a/tools/pe_bliss/resource_bitmap_reader.h b/tools/pe_bliss/resource_bitmap_reader.h
new file mode 100644
index 0000000000..f2b92bbde7
--- /dev/null
+++ b/tools/pe_bliss/resource_bitmap_reader.h
@@ -0,0 +1,50 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <string>
+#include "stdint_defs.h"
+
+namespace pe_bliss
+{
+class pe_resource_viewer;
+
+class resource_bitmap_reader
+{
+public:
+ resource_bitmap_reader(const pe_resource_viewer& res);
+
+ //Returns bitmap data by name and language (minimum checks of format correctness)
+ const std::string get_bitmap_by_name(uint32_t language, const std::wstring& name) const;
+ //Returns bitmap data by name and index in language directory (instead of language) (minimum checks of format correctness)
+ const std::string get_bitmap_by_name(const std::wstring& name, uint32_t index = 0) const;
+ //Returns bitmap data by ID and language (minimum checks of format correctness)
+ const std::string get_bitmap_by_id_lang(uint32_t language, uint32_t id) const;
+ //Returns bitmap data by ID and index in language directory (instead of language) (minimum checks of format correctness)
+ const std::string get_bitmap_by_id(uint32_t id, uint32_t index = 0) const;
+
+private:
+ //Helper function of creating bitmap header
+ static const std::string create_bitmap(const std::string& resource_data);
+
+ const pe_resource_viewer& res_;
+};
+}
diff --git a/tools/pe_bliss/resource_bitmap_writer.cpp b/tools/pe_bliss/resource_bitmap_writer.cpp
new file mode 100644
index 0000000000..3445a08445
--- /dev/null
+++ b/tools/pe_bliss/resource_bitmap_writer.cpp
@@ -0,0 +1,75 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "resource_bitmap_writer.h"
+#include "pe_resource_manager.h"
+#include "pe_structures.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+resource_bitmap_writer::resource_bitmap_writer(pe_resource_manager& res)
+ :res_(res)
+{}
+
+//Adds bitmap from bitmap file data. If bitmap already exists, replaces it
+//timestamp will be used for directories that will be added
+void resource_bitmap_writer::add_bitmap(const std::string& bitmap_file, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp)
+{
+ //Check bitmap data a little
+ if(bitmap_file.length() < sizeof(bitmapfileheader))
+ throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap);
+
+ resource_directory_entry new_entry;
+ new_entry.set_id(id);
+
+ //Add bitmap
+ res_.add_resource(bitmap_file.substr(sizeof(bitmapfileheader)), pe_resource_viewer::resource_bitmap, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp);
+}
+
+//Adds bitmap from bitmap file data. If bitmap already exists, replaces it
+//timestamp will be used for directories that will be added
+void resource_bitmap_writer::add_bitmap(const std::string& bitmap_file, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp)
+{
+ //Check bitmap data a little
+ if(bitmap_file.length() < sizeof(bitmapfileheader))
+ throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap);
+
+ resource_directory_entry new_entry;
+ new_entry.set_name(name);
+
+ //Add bitmap
+ res_.add_resource(bitmap_file.substr(sizeof(bitmapfileheader)), pe_resource_viewer::resource_bitmap, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp);
+}
+
+//Removes bitmap by name/ID and language
+bool resource_bitmap_writer::remove_bitmap(const std::wstring& name, uint32_t language)
+{
+ return res_.remove_resource(pe_resource_viewer::resource_bitmap, name, language);
+}
+
+//Removes bitmap by name/ID and language
+bool resource_bitmap_writer::remove_bitmap(uint32_t id, uint32_t language)
+{
+ return res_.remove_resource(pe_resource_viewer::resource_bitmap, id, language);
+}
+}
diff --git a/tools/pe_bliss/resource_bitmap_writer.h b/tools/pe_bliss/resource_bitmap_writer.h
new file mode 100644
index 0000000000..4b8ea72705
--- /dev/null
+++ b/tools/pe_bliss/resource_bitmap_writer.h
@@ -0,0 +1,47 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <string>
+#include "stdint_defs.h"
+
+namespace pe_bliss
+{
+class pe_resource_manager;
+
+class resource_bitmap_writer
+{
+public:
+ resource_bitmap_writer(pe_resource_manager& res);
+
+ //Adds bitmap from bitmap file data. If bitmap already exists, replaces it
+ //timestamp will be used for directories that will be added
+ void add_bitmap(const std::string& bitmap_file, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0);
+ void add_bitmap(const std::string& bitmap_file, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0);
+
+ //Removes bitmap by name/ID and language
+ bool remove_bitmap(const std::wstring& name, uint32_t language);
+ bool remove_bitmap(uint32_t id, uint32_t language);
+
+private:
+ pe_resource_manager& res_;
+};
+}
diff --git a/tools/pe_bliss/resource_cursor_icon_reader.cpp b/tools/pe_bliss/resource_cursor_icon_reader.cpp
new file mode 100644
index 0000000000..28a259163e
--- /dev/null
+++ b/tools/pe_bliss/resource_cursor_icon_reader.cpp
@@ -0,0 +1,521 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <algorithm>
+#include "resource_cursor_icon_reader.h"
+#include "pe_structures.h"
+#include "pe_resource_viewer.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+resource_cursor_icon_reader::resource_cursor_icon_reader(const pe_resource_viewer& res)
+ :res_(res)
+{}
+
+//Helper function of creating icon headers from ICON_GROUP resource data
+//Returns icon count
+uint16_t resource_cursor_icon_reader::format_icon_headers(std::string& ico_data, const std::string& resource_data)
+{
+ //Check resource data size
+ if(resource_data.length() < sizeof(ico_header))
+ throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon);
+
+ //Get icon header
+ const ico_header* info = reinterpret_cast<const ico_header*>(resource_data.data());
+
+ //Check resource data size
+ if(resource_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group))
+ throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon);
+
+ //Reserve memory to speed up a little
+ ico_data.reserve(sizeof(ico_header) + info->Count * sizeof(icondirentry));
+ ico_data.append(reinterpret_cast<const char*>(info), sizeof(ico_header));
+
+ //Iterate over all listed icons
+ uint32_t offset = sizeof(ico_header) + sizeof(icondirentry) * info->Count;
+ for(uint16_t i = 0; i != info->Count; ++i)
+ {
+ const icon_group* group = reinterpret_cast<const icon_group*>(resource_data.data() + sizeof(ico_header) + i * sizeof(icon_group));
+
+ //Fill icon data
+ icondirentry direntry;
+ direntry.BitCount = group->BitCount;
+ direntry.ColorCount = group->ColorCount;
+ direntry.Height = group->Height;
+ direntry.Planes = group->Planes;
+ direntry.Reserved = group->Reserved;
+ direntry.SizeInBytes = group->SizeInBytes;
+ direntry.Width = group->Width;
+ direntry.ImageOffset = offset;
+
+ //Add icon header to returned value
+ ico_data.append(reinterpret_cast<const char*>(&direntry), sizeof(icondirentry));
+
+ offset += group->SizeInBytes;
+ }
+
+ //Return icon count
+ return info->Count;
+}
+
+//Returns single icon data by ID and language (minimum checks of format correctness)
+const std::string resource_cursor_icon_reader::get_single_icon_by_id_lang(uint32_t language, uint32_t id) const
+{
+ //Get icon headers
+ std::string icon_data(lookup_icon_group_data_by_icon(id, language));
+ //Append icon data
+ icon_data.append(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, id).get_data());
+ return icon_data;
+}
+
+//Returns single icon data by ID and index in language directory (instead of language) (minimum checks of format correctness)
+const std::string resource_cursor_icon_reader::get_single_icon_by_id(uint32_t id, uint32_t index) const
+{
+ pe_resource_viewer::resource_language_list languages(res_.list_resource_languages(pe_resource_viewer::resource_icon, id));
+ if(languages.size() <= index)
+ throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found);
+
+ //Get icon headers
+ std::string icon_data(lookup_icon_group_data_by_icon(id, languages.at(index)));
+ //Append icon data
+ icon_data.append(res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, id, index).get_data());
+ return icon_data;
+}
+
+//Returns icon data by name and index in language directory (instead of language) (minimum checks of format correctness)
+const std::string resource_cursor_icon_reader::get_icon_by_name(const std::wstring& name, uint32_t index) const
+{
+ std::string ret;
+
+ //Get resource by name and index
+ const std::string data = res_.get_resource_data_by_name(pe_resource_viewer::resource_icon_group, name, index).get_data();
+
+ //Create icon headers
+ uint16_t icon_count = format_icon_headers(ret, data);
+
+ //Append icon data
+ for(uint16_t i = 0; i != icon_count; ++i)
+ {
+ const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group));
+ ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, group->Number, index).get_data();
+ }
+
+ return ret;
+}
+
+//Returns icon data by name and language (minimum checks of format correctness)
+const std::string resource_cursor_icon_reader::get_icon_by_name(uint32_t language, const std::wstring& name) const
+{
+ std::string ret;
+
+ //Get resource by name and language
+ const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, name).get_data();
+
+ //Create icon headers
+ uint16_t icon_count = format_icon_headers(ret, data);
+
+ //Append icon data
+ for(uint16_t i = 0; i != icon_count; ++i)
+ {
+ const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group));
+ ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, group->Number).get_data();
+ }
+
+ return ret;
+}
+
+//Returns icon data by ID and language (minimum checks of format correctness)
+const std::string resource_cursor_icon_reader::get_icon_by_id_lang(uint32_t language, uint32_t id) const
+{
+ std::string ret;
+
+ //Get resource by language and id
+ const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, id).get_data();
+
+ //Create icon headers
+ uint16_t icon_count = format_icon_headers(ret, data);
+
+ //Append icon data
+ for(uint16_t i = 0; i != icon_count; ++i)
+ {
+ const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group));
+ ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, group->Number).get_data();
+ }
+
+ return ret;
+}
+
+//Returns icon data by ID and index in language directory (instead of language) (minimum checks of format correctness)
+const std::string resource_cursor_icon_reader::get_icon_by_id(uint32_t id, uint32_t index) const
+{
+ std::string ret;
+
+ //Get resource by id and index
+ const std::string data = res_.get_resource_data_by_id(pe_resource_viewer::resource_icon_group, id, index).get_data();
+
+ //Create icon headers
+ uint16_t icon_count = format_icon_headers(ret, data);
+
+ //Append icon data
+ for(uint16_t i = 0; i != icon_count; ++i)
+ {
+ const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group));
+ ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, group->Number, index).get_data();
+ }
+
+ return ret;
+}
+
+//Checks for icon presence inside icon group, fills icon headers if found
+bool resource_cursor_icon_reader::check_icon_presence(const std::string& icon_group_resource_data, uint32_t icon_id, std::string& ico_data)
+{
+ //Check resource data size
+ if(icon_group_resource_data.length() < sizeof(ico_header))
+ throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon);
+
+ //Get icon header
+ const ico_header* info = reinterpret_cast<const ico_header*>(icon_group_resource_data.data());
+
+ //Check resource data size
+ if(icon_group_resource_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group))
+ throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon);
+
+ for(uint16_t i = 0; i != info->Count; ++i)
+ {
+ const icon_group* group = reinterpret_cast<const icon_group*>(icon_group_resource_data.data() + sizeof(ico_header) + i * sizeof(icon_group));
+ if(group->Number == icon_id)
+ {
+ //Reserve memory to speed up a little
+ ico_data.reserve(sizeof(ico_header) + sizeof(icondirentry));
+ //Write single-icon icon header
+ ico_header new_header = *info;
+ new_header.Count = 1;
+ ico_data.append(reinterpret_cast<const char*>(&new_header), sizeof(ico_header));
+
+ //Fill icon data
+ icondirentry direntry;
+ direntry.BitCount = group->BitCount;
+ direntry.ColorCount = group->ColorCount;
+ direntry.Height = group->Height;
+ direntry.Planes = group->Planes;
+ direntry.Reserved = group->Reserved;
+ direntry.SizeInBytes = group->SizeInBytes;
+ direntry.Width = group->Width;
+ direntry.ImageOffset = sizeof(ico_header) + sizeof(icondirentry);
+ ico_data.append(reinterpret_cast<const char*>(&direntry), sizeof(direntry));
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//Looks up icon group by icon id and returns full icon headers if found
+const std::string resource_cursor_icon_reader::lookup_icon_group_data_by_icon(uint32_t icon_id, uint32_t language) const
+{
+ std::string icon_header_data;
+
+ {
+ //List all ID-resources
+ pe_resource_viewer::resource_id_list ids(res_.list_resource_ids(pe_resource_viewer::resource_icon_group));
+
+ for(pe_resource_viewer::resource_id_list::const_iterator it = ids.begin(); it != ids.end(); ++it)
+ {
+ pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_icon_group, *it));
+ if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end()
+ && check_icon_presence(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, *it).get_data(), icon_id, icon_header_data))
+ return icon_header_data;
+ }
+ }
+
+ {
+ //List all named resources
+ pe_resource_viewer::resource_name_list names(res_.list_resource_names(pe_resource_viewer::resource_icon_group));
+ for(pe_resource_viewer::resource_name_list::const_iterator it = names.begin(); it != names.end(); ++it)
+ {
+ pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_icon_group, *it));
+ if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end()
+ && check_icon_presence(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, *it).get_data(), icon_id, icon_header_data))
+ return icon_header_data;
+ }
+ }
+
+ throw pe_exception("No icon group find for requested icon", pe_exception::no_icon_group_found);
+}
+
+//Returns single cursor data by ID and language (minimum checks of format correctness)
+const std::string resource_cursor_icon_reader::get_single_cursor_by_id_lang(uint32_t language, uint32_t id) const
+{
+ std::string raw_cursor_data(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, id).get_data());
+ //Get cursor headers
+ std::string cursor_data(lookup_cursor_group_data_by_cursor(id, language, raw_cursor_data));
+ //Append cursor data
+ cursor_data.append(raw_cursor_data.substr(sizeof(uint16_t) * 2 /* hotspot position */));
+ return cursor_data;
+}
+
+//Returns single cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness)
+const std::string resource_cursor_icon_reader::get_single_cursor_by_id(uint32_t id, uint32_t index) const
+{
+ pe_resource_viewer::resource_language_list languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor, id));
+ if(languages.size() <= index)
+ throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found);
+
+ std::string raw_cursor_data(res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, id, index).get_data());
+ //Get cursor headers
+ std::string cursor_data(lookup_cursor_group_data_by_cursor(id, languages.at(index), raw_cursor_data));
+ //Append cursor data
+ cursor_data.append(raw_cursor_data.substr(sizeof(uint16_t) * 2 /* hotspot position */));
+ return cursor_data;
+}
+
+//Helper function of creating cursor headers
+//Returns cursor count
+uint16_t resource_cursor_icon_reader::format_cursor_headers(std::string& cur_data, const std::string& resource_data, uint32_t language, uint32_t index) const
+{
+ //Check resource data length
+ if(resource_data.length() < sizeof(cursor_header))
+ throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor);
+
+ const cursor_header* info = reinterpret_cast<const cursor_header*>(resource_data.data());
+
+ //Check resource data length
+ if(resource_data.length() < sizeof(cursor_header) + sizeof(cursor_group) * info->Count)
+ throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor);
+
+ //Reserve needed space to speed up a little
+ cur_data.reserve(sizeof(cursor_header) + info->Count * sizeof(cursordirentry));
+ //Add cursor header
+ cur_data.append(reinterpret_cast<const char*>(info), sizeof(cursor_header));
+
+ //Iterate over all cursors listed in cursor group
+ uint32_t offset = sizeof(cursor_header) + sizeof(cursordirentry) * info->Count;
+ for(uint16_t i = 0; i != info->Count; ++i)
+ {
+ const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group));
+
+ //Fill cursor info
+ cursordirentry direntry;
+ direntry.ColorCount = 0; //OK
+ direntry.Width = static_cast<uint8_t>(group->Width);
+ direntry.Height = static_cast<uint8_t>(group->Height) / 2;
+ direntry.Reserved = 0;
+
+ //Now read hotspot data from cursor data directory
+ const std::string cursor = index == 0xFFFFFFFF
+ ? res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data()
+ : res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data();
+ if(cursor.length() < 2 * sizeof(uint16_t))
+ throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor);
+
+ //Here it is - two words in the very beginning of cursor data
+ direntry.HotspotX = *reinterpret_cast<const uint16_t*>(cursor.data());
+ direntry.HotspotY = *reinterpret_cast<const uint16_t*>(cursor.data() + sizeof(uint16_t));
+
+ //Fill the rest data
+ direntry.SizeInBytes = group->SizeInBytes - 2 * sizeof(uint16_t);
+ direntry.ImageOffset = offset;
+
+ //Add cursor header
+ cur_data.append(reinterpret_cast<const char*>(&direntry), sizeof(cursordirentry));
+
+ offset += direntry.SizeInBytes;
+ }
+
+ //Return cursor count
+ return info->Count;
+}
+
+//Returns cursor data by name and language (minimum checks of format correctness)
+const std::string resource_cursor_icon_reader::get_cursor_by_name(uint32_t language, const std::wstring& name) const
+{
+ std::string ret;
+
+ //Get resource by name and language
+ const std::string resource_data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, name).get_data();
+
+ //Create cursor headers
+ uint16_t cursor_count = format_cursor_headers(ret, resource_data, language);
+
+ //Add cursor data
+ for(uint16_t i = 0; i != cursor_count; ++i)
+ {
+ const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group));
+ ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data().substr(2 * sizeof(uint16_t));
+ }
+
+ return ret;
+}
+
+//Returns cursor data by name and index in language directory (instead of language) (minimum checks of format correctness)
+const std::string resource_cursor_icon_reader::get_cursor_by_name(const std::wstring& name, uint32_t index) const
+{
+ std::string ret;
+
+ //Get resource by name and index
+ const std::string resource_data = res_.get_resource_data_by_name(pe_resource_viewer::resource_cursor_group, name, index).get_data();
+
+ //Create cursor headers
+ uint16_t cursor_count = format_cursor_headers(ret, resource_data, 0, index);
+
+ //Add cursor data
+ for(uint16_t i = 0; i != cursor_count; ++i)
+ {
+ const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group));
+ ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data().substr(2 * sizeof(uint16_t));
+ }
+
+ return ret;
+}
+
+//Returns cursor data by ID and language (minimum checks of format correctness)
+const std::string resource_cursor_icon_reader::get_cursor_by_id_lang(uint32_t language, uint32_t id) const
+{
+ std::string ret;
+
+ //Get resource by ID and language
+ const std::string resource_data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, id).get_data();
+
+ //Create cursor headers
+ uint16_t cursor_count = format_cursor_headers(ret, resource_data, language);
+
+ //Add cursor data
+ for(uint16_t i = 0; i != cursor_count; ++i)
+ {
+ const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group));
+ ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data().substr(2 * sizeof(uint16_t));
+ }
+
+ return ret;
+}
+
+//Returns cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness)
+const std::string resource_cursor_icon_reader::get_cursor_by_id(uint32_t id, uint32_t index) const
+{
+ std::string ret;
+
+ //Get resource by ID and index
+ const std::string resource_data = res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor_group, id, index).get_data();
+
+ //Create cursor headers
+ uint16_t cursor_count = format_cursor_headers(ret, resource_data, 0, index);
+
+ //Add cursor data
+ for(uint16_t i = 0; i != cursor_count; ++i)
+ {
+ const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group));
+ ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data().substr(2 * sizeof(uint16_t));
+ }
+
+ return ret;
+}
+
+//Checks for cursor presence inside cursor group, fills cursor headers if found
+bool resource_cursor_icon_reader::check_cursor_presence(const std::string& cursor_group_resource_data, uint32_t cursor_id, std::string& cur_header_data, const std::string& raw_cursor_data)
+{
+ //Check resource data length
+ if(cursor_group_resource_data.length() < sizeof(cursor_header))
+ throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor);
+
+ const cursor_header* info = reinterpret_cast<const cursor_header*>(cursor_group_resource_data.data());
+
+ //Check resource data length
+ if(cursor_group_resource_data.length() < sizeof(cursor_header) + sizeof(cursor_group))
+ throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor);
+
+ //Iterate over all cursors listed in cursor group
+ for(uint16_t i = 0; i != info->Count; ++i)
+ {
+ const cursor_group* group = reinterpret_cast<const cursor_group*>(cursor_group_resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group));
+
+ if(group->Number == cursor_id)
+ {
+ //Reserve needed space to speed up a little
+ cur_header_data.reserve(sizeof(cursor_header) + sizeof(cursordirentry));
+ //Write single-cursor cursor header
+ cursor_header new_header = *info;
+ new_header.Count = 1;
+ cur_header_data.append(reinterpret_cast<const char*>(&new_header), sizeof(cursor_header));
+
+ //Fill cursor info
+ cursordirentry direntry;
+ direntry.ColorCount = 0; //OK
+ direntry.Width = static_cast<uint8_t>(group->Width);
+ direntry.Height = static_cast<uint8_t>(group->Height) / 2;
+ direntry.Reserved = 0;
+
+ if(raw_cursor_data.length() < 2 * sizeof(uint16_t))
+ throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor);
+
+ //Here it is - two words in the very beginning of cursor data
+ direntry.HotspotX = *reinterpret_cast<const uint16_t*>(raw_cursor_data.data());
+ direntry.HotspotY = *reinterpret_cast<const uint16_t*>(raw_cursor_data.data() + sizeof(uint16_t));
+
+ //Fill the rest data
+ direntry.SizeInBytes = group->SizeInBytes - 2 * sizeof(uint16_t);
+ direntry.ImageOffset = sizeof(cursor_header) + sizeof(cursordirentry);
+
+ //Add cursor header
+ cur_header_data.append(reinterpret_cast<const char*>(&direntry), sizeof(cursordirentry));
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//Looks up cursor group by cursor id and returns full cursor headers if found
+const std::string resource_cursor_icon_reader::lookup_cursor_group_data_by_cursor(uint32_t cursor_id, uint32_t language, const std::string& raw_cursor_data) const
+{
+ std::string cursor_header_data;
+
+ {
+ //List all ID-resources
+ pe_resource_viewer::resource_id_list ids(res_.list_resource_ids(pe_resource_viewer::resource_cursor_group));
+
+ for(pe_resource_viewer::resource_id_list::const_iterator it = ids.begin(); it != ids.end(); ++it)
+ {
+ pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor_group, *it));
+ if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end()
+ && check_cursor_presence(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, *it).get_data(), cursor_id, cursor_header_data, raw_cursor_data))
+ return cursor_header_data;
+ }
+ }
+
+ {
+ //List all named resources
+ pe_resource_viewer::resource_name_list names(res_.list_resource_names(pe_resource_viewer::resource_cursor_group));
+ for(pe_resource_viewer::resource_name_list::const_iterator it = names.begin(); it != names.end(); ++it)
+ {
+ pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor_group, *it));
+ if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end()
+ && check_cursor_presence(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, *it).get_data(), cursor_id, cursor_header_data, raw_cursor_data))
+ return cursor_header_data;
+ }
+ }
+
+ throw pe_exception("No cursor group find for requested icon", pe_exception::no_cursor_group_found);
+}
+}
diff --git a/tools/pe_bliss/resource_cursor_icon_reader.h b/tools/pe_bliss/resource_cursor_icon_reader.h
new file mode 100644
index 0000000000..e34fff419b
--- /dev/null
+++ b/tools/pe_bliss/resource_cursor_icon_reader.h
@@ -0,0 +1,84 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <string>
+#include "stdint_defs.h"
+
+namespace pe_bliss
+{
+class pe_resource_viewer;
+
+class resource_cursor_icon_reader
+{
+public:
+ resource_cursor_icon_reader(const pe_resource_viewer& res);
+
+ //Returns single icon data by ID and language (minimum checks of format correctness)
+ const std::string get_single_icon_by_id_lang(uint32_t language, uint32_t id) const;
+ //Returns single icon data by ID and index in language directory (instead of language) (minimum checks of format correctness)
+ const std::string get_single_icon_by_id(uint32_t id, uint32_t index = 0) const;
+
+ //Returns icon data of group of icons by name and language (minimum checks of format correctness)
+ const std::string get_icon_by_name(uint32_t language, const std::wstring& icon_group_name) const;
+ //Returns icon data of group of icons by name and index in language directory (instead of language) (minimum checks of format correctness)
+ const std::string get_icon_by_name(const std::wstring& icon_group_name, uint32_t index = 0) const;
+ //Returns icon data of group of icons by ID and language (minimum checks of format correctness)
+ const std::string get_icon_by_id_lang(uint32_t language, uint32_t icon_group_id) const;
+ //Returns icon data of group of icons by ID and index in language directory (instead of language) (minimum checks of format correctness)
+ const std::string get_icon_by_id(uint32_t icon_group_id, uint32_t index = 0) const;
+
+ //Returns single cursor data by ID and language (minimum checks of format correctness)
+ const std::string get_single_cursor_by_id_lang(uint32_t language, uint32_t id) const;
+ //Returns single cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness)
+ const std::string get_single_cursor_by_id(uint32_t id, uint32_t index = 0) const;
+
+ //Returns cursor data by name and language (minimum checks of format correctness)
+ const std::string get_cursor_by_name(uint32_t language, const std::wstring& cursor_group_name) const;
+ //Returns cursor data by name and index in language directory (instead of language) (minimum checks of format correctness)
+ const std::string get_cursor_by_name(const std::wstring& cursor_group_name, uint32_t index = 0) const;
+ //Returns cursor data by ID and language (minimum checks of format correctness)
+ const std::string get_cursor_by_id_lang(uint32_t language, uint32_t cursor_group_id) const;
+ //Returns cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness)
+ const std::string get_cursor_by_id(uint32_t cursor_group_id, uint32_t index = 0) const;
+
+private:
+ const pe_resource_viewer& res_;
+
+ //Helper function of creating icon headers from ICON_GROUP resource data
+ //Returns icon count
+ static uint16_t format_icon_headers(std::string& ico_data, const std::string& resource_data);
+
+ //Helper function of creating cursor headers from CURSOR_GROUP resource data
+ //Returns cursor count
+ uint16_t format_cursor_headers(std::string& cur_data, const std::string& resource_data, uint32_t language, uint32_t index = 0xFFFFFFFF) const;
+
+ //Looks up icon group by icon id and returns full icon headers if found
+ const std::string lookup_icon_group_data_by_icon(uint32_t icon_id, uint32_t language) const;
+ //Checks for icon presence inside icon group, fills icon headers if found
+ static bool check_icon_presence(const std::string& icon_group_resource_data, uint32_t icon_id, std::string& ico_data);
+
+ //Looks up cursor group by cursor id and returns full cursor headers if found
+ const std::string lookup_cursor_group_data_by_cursor(uint32_t cursor_id, uint32_t language, const std::string& raw_cursor_data) const;
+ //Checks for cursor presence inside cursor group, fills cursor headers if found
+ static bool check_cursor_presence(const std::string& icon_group_resource_data, uint32_t cursor_id, std::string& cur_header_data, const std::string& raw_cursor_data);
+};
+}
diff --git a/tools/pe_bliss/resource_cursor_icon_writer.cpp b/tools/pe_bliss/resource_cursor_icon_writer.cpp
new file mode 100644
index 0000000000..2f1c4363c4
--- /dev/null
+++ b/tools/pe_bliss/resource_cursor_icon_writer.cpp
@@ -0,0 +1,447 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <algorithm>
+#include <string.h>
+#include "resource_cursor_icon_writer.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+resource_cursor_icon_writer::resource_cursor_icon_writer(pe_resource_manager& res)
+ :res_(res)
+{}
+
+//Add icon helper
+void resource_cursor_icon_writer::add_icon(const std::string& icon_file, const resource_data_info* group_icon_info /* or zero */, resource_directory_entry& new_icon_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp)
+{
+ //Check icon for correctness
+ if(icon_file.length() < sizeof(ico_header))
+ throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon);
+
+ const ico_header* icon_header = reinterpret_cast<const ico_header*>(&icon_file[0]);
+
+ unsigned long size_of_headers = sizeof(ico_header) + icon_header->Count * sizeof(icondirentry);
+ if(icon_file.length() < size_of_headers || icon_header->Count == 0)
+ throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon);
+
+ //Enumerate all icons in file
+ for(uint16_t i = 0; i != icon_header->Count; ++i)
+ {
+ //Check icon entries
+ const icondirentry* icon_entry = reinterpret_cast<const icondirentry*>(&icon_file[sizeof(ico_header) + i * sizeof(icondirentry)]);
+ if(icon_entry->SizeInBytes == 0
+ || icon_entry->ImageOffset < size_of_headers
+ || !pe_utils::is_sum_safe(icon_entry->ImageOffset, icon_entry->SizeInBytes)
+ || icon_entry->ImageOffset + icon_entry->SizeInBytes > icon_file.length())
+ throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon);
+ }
+
+ std::string icon_group_data;
+ ico_header* info = 0;
+
+ if(group_icon_info)
+ {
+ //If icon group already exists
+ {
+ icon_group_data = group_icon_info->get_data();
+ codepage = group_icon_info->get_codepage(); //Don't change codepage of icon group entry
+ }
+
+ //Check resource data size
+ if(icon_group_data.length() < sizeof(ico_header))
+ throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon);
+
+ //Get icon header
+ info = reinterpret_cast<ico_header*>(&icon_group_data[0]);
+
+ //Check resource data size
+ if(icon_group_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group))
+ throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon);
+
+ icon_group_data.resize(sizeof(ico_header) + (info->Count + icon_header->Count) * sizeof(icon_group));
+ info = reinterpret_cast<ico_header*>(&icon_group_data[0]); //In case if memory was reallocated
+ }
+ else //Entry not found - icon group doesn't exist
+ {
+ icon_group_data.resize(sizeof(ico_header) + icon_header->Count * sizeof(icon_group));
+ memcpy(&icon_group_data[0], icon_header, sizeof(ico_header));
+ }
+
+ //Search for available icon IDs
+ std::vector<uint16_t> icon_id_list(get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_icon, mode, icon_header->Count));
+
+ //Enumerate all icons in file
+ for(uint16_t i = 0; i != icon_header->Count; ++i)
+ {
+ const icondirentry* icon_entry = reinterpret_cast<const icondirentry*>(&icon_file[sizeof(ico_header) + i * sizeof(icondirentry)]);
+ icon_group group = {0};
+
+ //Fill icon resource header
+ group.BitCount = icon_entry->BitCount;
+ group.ColorCount = icon_entry->ColorCount;
+ group.Height = icon_entry->Height;
+ group.Planes = icon_entry->Planes;
+ group.Reserved = icon_entry->Reserved;
+ group.SizeInBytes = icon_entry->SizeInBytes;
+ group.Width = icon_entry->Width;
+ group.Number = icon_id_list.at(i);
+
+ memcpy(&icon_group_data[sizeof(ico_header) + ((info ? info->Count : 0) + i) * sizeof(icon_group)], &group, sizeof(group));
+
+ //Add icon to resources
+ resource_directory_entry new_entry;
+ new_entry.set_id(group.Number);
+ res_.add_resource(icon_file.substr(icon_entry->ImageOffset, icon_entry->SizeInBytes), pe_resource_viewer::resource_icon, new_entry, resource_directory::entry_finder(group.Number), language, codepage, timestamp);
+ }
+
+ if(info)
+ info->Count += icon_header->Count; //Increase icon count, if we're adding icon to existing group
+
+ {
+ //Add or replace icon group data entry
+ res_.add_resource(icon_group_data, pe_resource_viewer::resource_icon_group, new_icon_group_entry, finder, language, codepage, timestamp);
+ }
+}
+
+//Returns free icon or cursor ID list depending on icon_place_mode
+const std::vector<uint16_t> resource_cursor_icon_writer::get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_type type, icon_place_mode mode, uint32_t count)
+{
+ //Search for available icon/cursor IDs
+ std::vector<uint16_t> icon_cursor_id_list;
+
+ try
+ {
+ //If any icon exists
+ //List icon IDs
+ std::vector<uint32_t> id_list(res_.list_resource_ids(type));
+ std::sort(id_list.begin(), id_list.end());
+
+ //If we are placing icon on free spaces
+ //I.e., icon IDs 1, 3, 4, 7, 8 already exist
+ //We'll place five icons on IDs 2, 5, 6, 9, 10
+ if(mode != icon_place_after_max_icon_id)
+ {
+ if(!id_list.empty())
+ {
+ //Determine and list free icon IDs
+ for(std::vector<uint32_t>::const_iterator it = id_list.begin(); it != id_list.end(); ++it)
+ {
+ if(it == id_list.begin())
+ {
+ if(*it > 1)
+ {
+ for(uint16_t i = 1; i != *it; ++i)
+ {
+ icon_cursor_id_list.push_back(i);
+ if(icon_cursor_id_list.size() == count)
+ break;
+ }
+ }
+ }
+ else if(*(it - 1) - *it > 1)
+ {
+ for(uint16_t i = static_cast<uint16_t>(*(it - 1) + 1); i != static_cast<uint16_t>(*it); ++i)
+ {
+ icon_cursor_id_list.push_back(i);
+ if(icon_cursor_id_list.size() == count)
+ break;
+ }
+ }
+
+ if(icon_cursor_id_list.size() == count)
+ break;
+ }
+ }
+ }
+
+ uint32_t max_id = id_list.empty() ? 0 : *std::max_element(id_list.begin(), id_list.end());
+ for(uint32_t i = static_cast<uint32_t>(icon_cursor_id_list.size()); i != count; ++i)
+ icon_cursor_id_list.push_back(static_cast<uint16_t>(++max_id));
+ }
+ catch(const pe_exception&) //Entry not found
+ {
+ for(uint16_t i = 1; i != count + 1; ++i)
+ icon_cursor_id_list.push_back(i);
+ }
+
+ return icon_cursor_id_list;
+}
+
+//Add cursor helper
+void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, const resource_data_info* group_cursor_info /* or zero */, resource_directory_entry& new_cursor_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp)
+{
+ //Check cursor for correctness
+ if(cursor_file.length() < sizeof(cursor_header))
+ throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor);
+
+ const cursor_header* cur_header = reinterpret_cast<const cursor_header*>(&cursor_file[0]);
+
+ unsigned long size_of_headers = sizeof(cursor_header) + cur_header->Count * sizeof(cursordirentry);
+ if(cursor_file.length() < size_of_headers || cur_header->Count == 0)
+ throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor);
+
+ //Enumerate all cursors in file
+ for(uint16_t i = 0; i != cur_header->Count; ++i)
+ {
+ //Check cursor entries
+ const cursordirentry* cursor_entry = reinterpret_cast<const cursordirentry*>(&cursor_file[sizeof(cursor_header) + i * sizeof(cursordirentry)]);
+ if(cursor_entry->SizeInBytes == 0
+ || cursor_entry->ImageOffset < size_of_headers
+ || !pe_utils::is_sum_safe(cursor_entry->ImageOffset, cursor_entry->SizeInBytes)
+ || cursor_entry->ImageOffset + cursor_entry->SizeInBytes > cursor_file.length())
+ throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor);
+ }
+
+ std::string cursor_group_data;
+ cursor_header* info = 0;
+
+ if(group_cursor_info)
+ {
+ //If cursor group already exists
+ {
+ cursor_group_data = group_cursor_info->get_data();
+ codepage = group_cursor_info->get_codepage(); //Don't change codepage of cursor group entry
+ }
+
+ //Check resource data size
+ if(cursor_group_data.length() < sizeof(cursor_header))
+ throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor);
+
+ //Get cursor header
+ info = reinterpret_cast<cursor_header*>(&cursor_group_data[0]);
+
+ //Check resource data size
+ if(cursor_group_data.length() < sizeof(cursor_header) + info->Count * sizeof(cursor_group))
+ throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor);
+
+ cursor_group_data.resize(sizeof(cursor_header) + (info->Count + cur_header->Count) * sizeof(cursor_group));
+ info = reinterpret_cast<cursor_header*>(&cursor_group_data[0]); //In case if memory was reallocated
+ }
+ else //Entry not found - cursor group doesn't exist
+ {
+ cursor_group_data.resize(sizeof(cursor_header) + cur_header->Count * sizeof(cursor_group));
+ memcpy(&cursor_group_data[0], cur_header, sizeof(cursor_header));
+ }
+
+ //Search for available cursor IDs
+ std::vector<uint16_t> cursor_id_list(get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_cursor, mode, cur_header->Count));
+
+ //Enumerate all cursors in file
+ for(uint16_t i = 0; i != cur_header->Count; ++i)
+ {
+ const cursordirentry* cursor_entry = reinterpret_cast<const cursordirentry*>(&cursor_file[sizeof(cursor_header) + i * sizeof(cursordirentry)]);
+ cursor_group group = {0};
+
+ //Fill cursor resource header
+ group.Height = cursor_entry->Height * 2;
+ group.SizeInBytes = cursor_entry->SizeInBytes + 2 * sizeof(uint16_t) /* hotspot coordinates */;
+ group.Width = cursor_entry->Width;
+ group.Number = cursor_id_list.at(i);
+
+ memcpy(&cursor_group_data[sizeof(cursor_header) + ((info ? info->Count : 0) + i) * sizeof(cursor_group)], &group, sizeof(group));
+
+ //Add cursor to resources
+ resource_directory_entry new_entry;
+ new_entry.set_id(group.Number);
+
+ //Fill resource data (two WORDs for hotspot of cursor, and cursor bitmap data)
+ std::string cur_data;
+ cur_data.resize(sizeof(uint16_t) * 2);
+ memcpy(&cur_data[0], &cursor_entry->HotspotX, sizeof(uint16_t));
+ memcpy(&cur_data[sizeof(uint16_t)], &cursor_entry->HotspotY, sizeof(uint16_t));
+ cur_data.append(cursor_file.substr(cursor_entry->ImageOffset, cursor_entry->SizeInBytes));
+
+ res_.add_resource(cur_data, pe_resource_viewer::resource_cursor, new_entry, resource_directory::entry_finder(group.Number), language, codepage, timestamp);
+ }
+
+ if(info)
+ info->Count += cur_header->Count; //Increase cursor count, if we're adding cursor to existing group
+
+ {
+ //Add or replace cursor group data entry
+ res_.add_resource(cursor_group_data, pe_resource_viewer::resource_cursor_group, new_cursor_group_entry, finder, language, codepage, timestamp);
+ }
+}
+
+//Adds icon(s) from icon file data
+//timestamp will be used for directories that will be added
+//If icon group with name "icon_group_name" or ID "icon_group_id" already exists, it will be appended with new icon(s)
+//(Codepage of icon group and icons will not be changed in this case)
+//icon_place_mode determines, how new icon(s) will be placed
+void resource_cursor_icon_writer::add_icon(const std::string& icon_file, const std::wstring& icon_group_name, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp)
+{
+ resource_directory_entry new_icon_group_entry;
+ new_icon_group_entry.set_name(icon_group_name);
+ std::auto_ptr<resource_data_info> data_info;
+
+ try
+ {
+ data_info.reset(new resource_data_info(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, icon_group_name)));
+ }
+ catch(const pe_exception&) //Entry not found
+ {
+ }
+
+ add_icon(icon_file, data_info.get(), new_icon_group_entry, resource_directory::entry_finder(icon_group_name), language, mode, codepage, timestamp);
+}
+
+void resource_cursor_icon_writer::add_icon(const std::string& icon_file, uint32_t icon_group_id, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp)
+{
+ resource_directory_entry new_icon_group_entry;
+ new_icon_group_entry.set_id(icon_group_id);
+ std::auto_ptr<resource_data_info> data_info;
+
+ try
+ {
+ data_info.reset(new resource_data_info(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, icon_group_id)));
+ }
+ catch(const pe_exception&) //Entry not found
+ {
+ }
+
+ add_icon(icon_file, data_info.get(), new_icon_group_entry, resource_directory::entry_finder(icon_group_id), language, mode, codepage, timestamp);
+}
+
+//Adds cursor(s) from cursor file data
+//timestamp will be used for directories that will be added
+//If cursor group with name "cursor_group_name" or ID "cursor_group_id" already exists, it will be appended with new cursor(s)
+//(Codepage of cursor group and cursors will not be changed in this case)
+//icon_place_mode determines, how new cursor(s) will be placed
+void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, const std::wstring& cursor_group_name, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp)
+{
+ resource_directory_entry new_cursor_group_entry;
+ new_cursor_group_entry.set_name(cursor_group_name);
+ std::auto_ptr<resource_data_info> data_info;
+
+ try
+ {
+ data_info.reset(new resource_data_info(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, cursor_group_name)));
+ }
+ catch(const pe_exception&) //Entry not found
+ {
+ }
+
+ add_cursor(cursor_file, data_info.get(), new_cursor_group_entry, resource_directory::entry_finder(cursor_group_name), language, mode, codepage, timestamp);
+}
+
+void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, uint32_t cursor_group_id, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp)
+{
+ resource_directory_entry new_cursor_group_entry;
+ new_cursor_group_entry.set_id(cursor_group_id);
+ std::auto_ptr<resource_data_info> data_info;
+
+ try
+ {
+ data_info.reset(new resource_data_info(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, cursor_group_id)));
+ }
+ catch(const pe_exception&) //Entry not found
+ {
+ }
+
+ add_cursor(cursor_file, data_info.get(), new_cursor_group_entry, resource_directory::entry_finder(cursor_group_id), language, mode, codepage, timestamp);
+}
+
+//Remove icon group helper
+void resource_cursor_icon_writer::remove_icons_from_icon_group(const std::string& icon_group_data, uint32_t language)
+{
+ //Check resource data size
+ if(icon_group_data.length() < sizeof(ico_header))
+ throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon);
+
+ //Get icon header
+ const ico_header* info = reinterpret_cast<const ico_header*>(icon_group_data.data());
+
+ uint16_t icon_count = info->Count;
+
+ //Check resource data size
+ if(icon_group_data.length() < sizeof(ico_header) + icon_count * sizeof(icon_group))
+ throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon);
+
+ //Remove icon data
+ for(uint16_t i = 0; i != icon_count; ++i)
+ {
+ const icon_group* group = reinterpret_cast<const icon_group*>(icon_group_data.data() + sizeof(ico_header) + i * sizeof(icon_group));
+ res_.remove_resource(pe_resource_viewer::resource_icon, group->Number, language);
+ }
+}
+
+//Remove cursor group helper
+void resource_cursor_icon_writer::remove_cursors_from_cursor_group(const std::string& cursor_group_data, uint32_t language)
+{
+ //Check resource data size
+ if(cursor_group_data.length() < sizeof(cursor_header))
+ throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor);
+
+ //Get icon header
+ const cursor_header* info = reinterpret_cast<const cursor_header*>(cursor_group_data.data());
+
+ uint16_t cursor_count = info->Count;
+
+ //Check resource data size
+ if(cursor_group_data.length() < sizeof(cursor_header) + cursor_count * sizeof(cursor_group))
+ throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor);
+
+ //Remove icon data
+ for(uint16_t i = 0; i != cursor_count; ++i)
+ {
+ const icon_group* group = reinterpret_cast<const icon_group*>(cursor_group_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group));
+ res_.remove_resource(pe_resource_viewer::resource_cursor, group->Number, language);
+ }
+}
+
+//Removes cursor group and all its cursors by name/ID and language
+bool resource_cursor_icon_writer::remove_cursor_group(const std::wstring& cursor_group_name, uint32_t language)
+{
+ //Get resource by name and language
+ const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, cursor_group_name).get_data();
+ remove_cursors_from_cursor_group(data, language);
+ return res_.remove_resource(pe_resource_viewer::resource_cursor_group, cursor_group_name, language);
+}
+
+//Removes cursor group and all its cursors by name/ID and language
+bool resource_cursor_icon_writer::remove_cursor_group(uint32_t cursor_group_id, uint32_t language)
+{
+ //Get resource by name and language
+ const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, cursor_group_id).get_data();
+ remove_cursors_from_cursor_group(data, language);
+ return res_.remove_resource(pe_resource_viewer::resource_cursor_group, cursor_group_id, language);
+}
+
+//Removes icon group and all its icons by name/ID and language
+bool resource_cursor_icon_writer::remove_icon_group(const std::wstring& icon_group_name, uint32_t language)
+{
+ //Get resource by name and language
+ const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, icon_group_name).get_data();
+ remove_icons_from_icon_group(data, language);
+ return res_.remove_resource(pe_resource_viewer::resource_icon_group, icon_group_name, language);
+}
+
+//Removes icon group and all its icons by name/ID and language
+bool resource_cursor_icon_writer::remove_icon_group(uint32_t icon_group_id, uint32_t language)
+{
+ //Get resource by name and language
+ const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, icon_group_id).get_data();
+ remove_icons_from_icon_group(data, language);
+ return res_.remove_resource(pe_resource_viewer::resource_icon_group, icon_group_id, language);
+}
+}
diff --git a/tools/pe_bliss/resource_cursor_icon_writer.h b/tools/pe_bliss/resource_cursor_icon_writer.h
new file mode 100644
index 0000000000..e73ac6a093
--- /dev/null
+++ b/tools/pe_bliss/resource_cursor_icon_writer.h
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <string>
+#include <vector>
+#include "stdint_defs.h"
+#include "pe_resource_manager.h"
+
+namespace pe_bliss
+{
+class pe_resource_manager;
+
+class resource_cursor_icon_writer
+{
+public:
+ //Determines, how new icon(s) or cursor(s) will be placed
+ enum icon_place_mode
+ {
+ icon_place_after_max_icon_id, //Icon(s) will be placed after all existing
+ icon_place_free_ids //New icon(s) will take all free IDs between existing icons
+ };
+
+public:
+ resource_cursor_icon_writer(pe_resource_manager& res);
+
+ //Removes icon group and all its icons by name/ID and language
+ bool remove_icon_group(const std::wstring& icon_group_name, uint32_t language);
+ bool remove_icon_group(uint32_t icon_group_id, uint32_t language);
+
+ //Adds icon(s) from icon file data
+ //timestamp will be used for directories that will be added
+ //If icon group with name "icon_group_name" or ID "icon_group_id" already exists, it will be appended with new icon(s)
+ //(Codepage of icon group and icons will not be changed in this case)
+ //icon_place_mode determines, how new icon(s) will be placed
+ void add_icon(const std::string& icon_file,
+ const std::wstring& icon_group_name,
+ uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id,
+ uint32_t codepage = 0, uint32_t timestamp = 0);
+
+ void add_icon(const std::string& icon_file,
+ uint32_t icon_group_id,
+ uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id,
+ uint32_t codepage = 0, uint32_t timestamp = 0);
+
+ //Removes cursor group and all its cursors by name/ID and language
+ bool remove_cursor_group(const std::wstring& cursor_group_name, uint32_t language);
+ bool remove_cursor_group(uint32_t cursor_group_id, uint32_t language);
+
+ //Adds cursor(s) from cursor file data
+ //timestamp will be used for directories that will be added
+ //If cursor group with name "cursor_group_name" or ID "cursor_group_id" already exists, it will be appended with new cursor(s)
+ //(Codepage of cursor group and cursors will not be changed in this case)
+ //icon_place_mode determines, how new cursor(s) will be placed
+ void add_cursor(const std::string& cursor_file, const std::wstring& cursor_group_name, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0);
+ void add_cursor(const std::string& cursor_file, uint32_t cursor_group_id, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0);
+
+private:
+ pe_resource_manager& res_;
+
+ //Add icon helper
+ void add_icon(const std::string& icon_file, const resource_data_info* group_icon_info /* or zero */, resource_directory_entry& new_icon_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp);
+
+ //Remove icon group helper
+ void remove_icons_from_icon_group(const std::string& icon_group_data, uint32_t language);
+
+ //Add cursor helper
+ void add_cursor(const std::string& cursor_file, const resource_data_info* group_cursor_info /* or zero */, resource_directory_entry& new_cursor_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp);
+
+ //Remove cursor group helper
+ void remove_cursors_from_cursor_group(const std::string& cursor_group_data, uint32_t language);
+
+ //Returns free icon or cursor ID list depending on icon_place_mode
+ const std::vector<uint16_t> get_icon_or_cursor_free_id_list(pe_resource_manager::resource_type type, icon_place_mode mode, uint32_t count);
+};
+}
diff --git a/tools/pe_bliss/resource_data_info.cpp b/tools/pe_bliss/resource_data_info.cpp
new file mode 100644
index 0000000000..75bb060eae
--- /dev/null
+++ b/tools/pe_bliss/resource_data_info.cpp
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "resource_data_info.h"
+#include "pe_resource_viewer.h"
+
+namespace pe_bliss
+{
+//Default constructor
+resource_data_info::resource_data_info(const std::string& data, uint32_t codepage)
+ :data_(data), codepage_(codepage)
+{}
+
+//Constructor from data
+resource_data_info::resource_data_info(const resource_data_entry& data)
+ :data_(data.get_data()), codepage_(data.get_codepage())
+{}
+
+//Returns resource data
+const std::string& resource_data_info::get_data() const
+{
+ return data_;
+}
+
+//Returns resource codepage
+uint32_t resource_data_info::get_codepage() const
+{
+ return codepage_;
+}
+}
diff --git a/tools/pe_bliss/resource_data_info.h b/tools/pe_bliss/resource_data_info.h
new file mode 100644
index 0000000000..e2275ebbf5
--- /dev/null
+++ b/tools/pe_bliss/resource_data_info.h
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <string>
+#include "stdint_defs.h"
+
+namespace pe_bliss
+{
+class resource_data_entry;
+
+//Class representing resource data
+class resource_data_info
+{
+public:
+ //Constructor from data
+ resource_data_info(const std::string& data, uint32_t codepage);
+ //Constructor from data
+ explicit resource_data_info(const resource_data_entry& data);
+
+ //Returns resource data
+ const std::string& get_data() const;
+ //Returns resource codepage
+ uint32_t get_codepage() const;
+
+private:
+ std::string data_;
+ uint32_t codepage_;
+};
+}
diff --git a/tools/pe_bliss/resource_internal.h b/tools/pe_bliss/resource_internal.h
new file mode 100644
index 0000000000..64a5bf3903
--- /dev/null
+++ b/tools/pe_bliss/resource_internal.h
@@ -0,0 +1,34 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+
+#define U16TEXT(t) reinterpret_cast<const unicode16_t*>( t )
+
+#define StringFileInfo U16TEXT("S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0")
+#define SizeofStringFileInfo sizeof("S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0")
+#define VarFileInfo U16TEXT("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0")
+#define Translation U16TEXT("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0")
+
+#define VarFileInfoAligned U16TEXT("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0\0")
+#define TranslationAligned U16TEXT("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0\0\0")
+#define SizeofVarFileInfoAligned sizeof("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0\0")
+#define SizeofTranslationAligned sizeof("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0\0\0")
diff --git a/tools/pe_bliss/resource_message_list_reader.cpp b/tools/pe_bliss/resource_message_list_reader.cpp
new file mode 100644
index 0000000000..f2ea142bee
--- /dev/null
+++ b/tools/pe_bliss/resource_message_list_reader.cpp
@@ -0,0 +1,131 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "resource_message_list_reader.h"
+#include "pe_resource_viewer.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+resource_message_list_reader::resource_message_list_reader(const pe_resource_viewer& res)
+ :res_(res)
+{}
+
+//Helper function of parsing message list table
+const resource_message_list resource_message_list_reader::parse_message_list(const std::string& resource_data)
+{
+ resource_message_list ret;
+
+ //Check resource data length
+ if(resource_data.length() < sizeof(message_resource_data))
+ throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table);
+
+ const message_resource_data* message_data = reinterpret_cast<const message_resource_data*>(resource_data.data());
+
+ //Check resource data length more carefully and some possible overflows
+ if(message_data->NumberOfBlocks >= pe_utils::max_dword / sizeof(message_resource_block)
+ || !pe_utils::is_sum_safe(message_data->NumberOfBlocks * sizeof(message_resource_block), sizeof(message_resource_data))
+ || resource_data.length() < message_data->NumberOfBlocks * sizeof(message_resource_block) + sizeof(message_resource_data))
+ throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table);
+
+ //Iterate over all message resource blocks
+ for(unsigned long i = 0; i != message_data->NumberOfBlocks; ++i)
+ {
+ //Get block
+ const message_resource_block* block =
+ reinterpret_cast<const message_resource_block*>(resource_data.data() + sizeof(message_resource_data) - sizeof(message_resource_block) + sizeof(message_resource_block) * i);
+
+ //Check resource data length and IDs
+ if(resource_data.length() < block->OffsetToEntries || block->LowId > block->HighId)
+ throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table);
+
+ unsigned long current_pos = 0;
+ static const unsigned long size_of_entry_headers = 4;
+ //List all message resource entries in block
+ for(uint32_t curr_id = block->LowId; curr_id <= block->HighId; curr_id++)
+ {
+ //Check resource data length and some possible overflows
+ if(!pe_utils::is_sum_safe(block->OffsetToEntries, current_pos)
+ || !pe_utils::is_sum_safe(block->OffsetToEntries + current_pos, size_of_entry_headers)
+ || resource_data.length() < block->OffsetToEntries + current_pos + size_of_entry_headers)
+ throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table);
+
+ //Get entry
+ const message_resource_entry* entry = reinterpret_cast<const message_resource_entry*>(resource_data.data() + block->OffsetToEntries + current_pos);
+
+ //Check resource data length and entry length and some possible overflows
+ if(entry->Length < size_of_entry_headers
+ || !pe_utils::is_sum_safe(block->OffsetToEntries + current_pos, entry->Length)
+ || resource_data.length() < block->OffsetToEntries + current_pos + entry->Length
+ || entry->Length < size_of_entry_headers)
+ throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table);
+
+ if(entry->Flags & message_resource_unicode)
+ {
+ //If string is UNICODE
+ //Check its length
+ if(entry->Length % 2)
+ throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table);
+
+ //Add ID and string to message table
+#ifdef PE_BLISS_WINDOWS
+ ret.insert(std::make_pair(curr_id, message_table_item(
+ std::wstring(reinterpret_cast<const wchar_t*>(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers),
+ (entry->Length - size_of_entry_headers) / 2)
+ )));
+#else
+ ret.insert(std::make_pair(curr_id, message_table_item(
+ pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers),
+ (entry->Length - size_of_entry_headers) / 2))
+ )));
+#endif
+ }
+ else
+ {
+ //If string is ANSI
+ //Add ID and string to message table
+ ret.insert(std::make_pair(curr_id, message_table_item(
+ std::string(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers,
+ entry->Length - size_of_entry_headers)
+ )));
+ }
+
+ //Go to next entry
+ current_pos += entry->Length;
+ }
+ }
+
+ return ret;
+}
+
+//Returns message table data by ID and index in language directory (instead of language)
+const resource_message_list resource_message_list_reader::get_message_table_by_id(uint32_t id, uint32_t index) const
+{
+ return parse_message_list(res_.get_resource_data_by_id(pe_resource_viewer::resource_message_table, id, index).get_data());
+}
+
+//Returns message table data by ID and language
+const resource_message_list resource_message_list_reader::get_message_table_by_id_lang(uint32_t language, uint32_t id) const
+{
+ return parse_message_list(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_message_table, id).get_data());
+}
+}
diff --git a/tools/pe_bliss/resource_message_list_reader.h b/tools/pe_bliss/resource_message_list_reader.h
new file mode 100644
index 0000000000..a0ac96eb8c
--- /dev/null
+++ b/tools/pe_bliss/resource_message_list_reader.h
@@ -0,0 +1,49 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include "message_table.h"
+
+namespace pe_bliss
+{
+class pe_resource_viewer;
+
+//ID; message_table_item
+typedef std::map<uint32_t, message_table_item> resource_message_list;
+
+class resource_message_list_reader
+{
+public:
+ resource_message_list_reader(const pe_resource_viewer& res);
+
+ //Returns message table data by ID and language
+ const resource_message_list get_message_table_by_id_lang(uint32_t language, uint32_t id) const;
+ //Returns message table data by ID and index in language directory (instead of language)
+ const resource_message_list get_message_table_by_id(uint32_t id, uint32_t index = 0) const;
+
+ //Helper function of parsing message list table
+ //resource_data - raw message table resource data
+ static const resource_message_list parse_message_list(const std::string& resource_data);
+
+private:
+ const pe_resource_viewer& res_;
+};
+}
diff --git a/tools/pe_bliss/resource_string_table_reader.cpp b/tools/pe_bliss/resource_string_table_reader.cpp
new file mode 100644
index 0000000000..8a51720e6a
--- /dev/null
+++ b/tools/pe_bliss/resource_string_table_reader.cpp
@@ -0,0 +1,109 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "resource_string_table_reader.h"
+#include "pe_resource_viewer.h"
+
+namespace pe_bliss
+{
+resource_string_table_reader::resource_string_table_reader(const pe_resource_viewer& res)
+ :res_(res)
+{}
+
+//Returns string table data by ID and index in language directory (instead of language)
+const resource_string_list resource_string_table_reader::get_string_table_by_id(uint32_t id, uint32_t index) const
+{
+ return parse_string_list(id, res_.get_resource_data_by_id(pe_resource_viewer::resource_string, id, index).get_data());
+}
+
+//Returns string table data by ID and language
+const resource_string_list resource_string_table_reader::get_string_table_by_id_lang(uint32_t language, uint32_t id) const
+{
+ return parse_string_list(id, res_.get_resource_data_by_id(language, pe_resource_viewer::resource_string, id).get_data());
+}
+
+//Helper function of parsing string list table
+const resource_string_list resource_string_table_reader::parse_string_list(uint32_t id, const std::string& resource_data)
+{
+ resource_string_list ret;
+
+ //16 is maximum count of strings in a string table
+ static const unsigned long max_string_list_entries = 16;
+ unsigned long passed_bytes = 0;
+ for(unsigned long i = 0; i != max_string_list_entries; ++i)
+ {
+ //Check resource data length
+ if(resource_data.length() < sizeof(uint16_t) + passed_bytes)
+ throw pe_exception("Incorrect resource string table", pe_exception::resource_incorrect_string_table);
+
+ //Get string length - the first WORD
+ uint16_t string_length = *reinterpret_cast<const uint16_t*>(resource_data.data() + passed_bytes);
+ passed_bytes += sizeof(uint16_t); //WORD containing string length
+
+ //Check resource data length again
+ if(resource_data.length() < string_length + passed_bytes)
+ throw pe_exception("Incorrect resource string table", pe_exception::resource_incorrect_string_table);
+
+ if(string_length)
+ {
+ //Create and save string (UNICODE)
+#ifdef PE_BLISS_WINDOWS
+ ret.insert(
+ std::make_pair(static_cast<uint16_t>(((id - 1) << 4) + i), //ID of string is calculated such way
+ std::wstring(reinterpret_cast<const wchar_t*>(resource_data.data() + passed_bytes), string_length)));
+#else
+ ret.insert(
+ std::make_pair(static_cast<uint16_t>(((id - 1) << 4) + i), //ID of string is calculated such way
+ pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + passed_bytes), string_length))));
+#endif
+ }
+
+ //Go to next string
+ passed_bytes += string_length * 2;
+ }
+
+ return ret;
+}
+
+//Returns string from string table by ID and language
+const std::wstring resource_string_table_reader::get_string_by_id_lang(uint32_t language, uint16_t id) const
+{
+ //List strings by string table id and language
+ const resource_string_list strings(get_string_table_by_id_lang(language, (id >> 4) + 1));
+ resource_string_list::const_iterator it = strings.find(id); //Find string by id
+ if(it == strings.end())
+ throw pe_exception("Resource string not found", pe_exception::resource_string_not_found);
+
+ return (*it).second;
+}
+
+//Returns string from string table by ID and index in language directory (instead of language)
+const std::wstring resource_string_table_reader::get_string_by_id(uint16_t id, uint32_t index) const
+{
+ //List strings by string table id and index
+ const resource_string_list strings(get_string_table_by_id((id >> 4) + 1, index));
+ resource_string_list::const_iterator it = strings.find(id); //Find string by id
+ if(it == strings.end())
+ throw pe_exception("Resource string not found", pe_exception::resource_string_not_found);
+
+ return (*it).second;
+}
+}
diff --git a/tools/pe_bliss/resource_string_table_reader.h b/tools/pe_bliss/resource_string_table_reader.h
new file mode 100644
index 0000000000..e3ded1da85
--- /dev/null
+++ b/tools/pe_bliss/resource_string_table_reader.h
@@ -0,0 +1,57 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <string>
+#include <map>
+#include "stdint_defs.h"
+
+namespace pe_bliss
+{
+class pe_resource_viewer;
+
+//ID; string
+typedef std::map<uint16_t, std::wstring> resource_string_list;
+
+class resource_string_table_reader
+{
+public:
+ resource_string_table_reader(const pe_resource_viewer& res);
+
+public:
+ //Returns string table data by ID and language
+ const resource_string_list get_string_table_by_id_lang(uint32_t language, uint32_t id) const;
+ //Returns string table data by ID and index in language directory (instead of language)
+ const resource_string_list get_string_table_by_id(uint32_t id, uint32_t index = 0) const;
+ //Returns string from string table by ID and language
+ const std::wstring get_string_by_id_lang(uint32_t language, uint16_t id) const;
+ //Returns string from string table by ID and index in language directory (instead of language)
+ const std::wstring get_string_by_id(uint16_t id, uint32_t index = 0) const;
+
+private:
+ const pe_resource_viewer& res_;
+
+ //Helper function of parsing string list table
+ //Id of resource is needed to calculate string IDs correctly
+ //resource_data is raw string table resource data
+ static const resource_string_list parse_string_list(uint32_t id, const std::string& resource_data);
+};
+}
diff --git a/tools/pe_bliss/resource_version_info_reader.cpp b/tools/pe_bliss/resource_version_info_reader.cpp
new file mode 100644
index 0000000000..8ad44c6856
--- /dev/null
+++ b/tools/pe_bliss/resource_version_info_reader.cpp
@@ -0,0 +1,311 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "resource_version_info_reader.h"
+#include "utils.h"
+#include "pe_exception.h"
+#include "resource_internal.h"
+#include "pe_resource_viewer.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+//Root version info block key value
+const u16string resource_version_info_reader::version_info_key(U16TEXT("V\0S\0_\0V\0E\0R\0S\0I\0O\0N\0_\0I\0N\0F\0O\0\0"));
+
+resource_version_info_reader::resource_version_info_reader(const pe_resource_viewer& res)
+ :res_(res)
+{}
+
+//Returns aligned version block value position
+uint32_t resource_version_info_reader::get_version_block_value_pos(uint32_t base_pos, const unicode16_t* key)
+{
+ uint32_t string_length = static_cast<uint32_t>(u16string(key).length());
+ uint32_t ret = pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 /* headers before Key data */
+ + base_pos
+ + (string_length + 1 /* nullbyte */) * 2),
+ sizeof(uint32_t));
+
+ //Check possible overflows
+ if(ret < base_pos || ret < sizeof(uint16_t) * 3 || ret < (string_length + 1) * 2)
+ throw_incorrect_version_info();
+
+ return ret;
+}
+
+//Returns aligned version block first child position
+uint32_t resource_version_info_reader::get_version_block_first_child_pos(uint32_t base_pos, uint32_t value_length, const unicode16_t* key)
+{
+ uint32_t string_length = static_cast<uint32_t>(u16string(key).length());
+ uint32_t ret = pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 /* headers before Key data */
+ + base_pos
+ + (string_length + 1 /* nullbyte */) * 2),
+ sizeof(uint32_t))
+ + pe_utils::align_up(value_length, sizeof(uint32_t));
+
+ //Check possible overflows
+ if(ret < base_pos || ret < value_length || ret < sizeof(uint16_t) * 3 || ret < (string_length + 1) * 2)
+ throw_incorrect_version_info();
+
+ return ret;
+}
+
+//Throws an exception (id = resource_incorrect_version_info)
+void resource_version_info_reader::throw_incorrect_version_info()
+{
+ throw pe_exception("Incorrect resource version info", pe_exception::resource_incorrect_version_info);
+}
+
+//Returns full version information:
+//file_version_info: versions and file info
+//lang_string_values_map: map of version info strings with encodings
+//translation_values_map: map of translations
+const file_version_info resource_version_info_reader::get_version_info(lang_string_values_map& string_values, translation_values_map& translations, const std::string& resource_data) const
+{
+ //Fixed file version info
+ file_version_info ret;
+
+ //Check resource data length
+ if(resource_data.length() < sizeof(version_info_block))
+ throw_incorrect_version_info();
+
+ //Root version info block
+ const version_info_block* root_block = reinterpret_cast<const version_info_block*>(resource_data.data());
+
+ //Check root block key for null-termination and its name
+ if(!pe_utils::is_null_terminated(root_block->Key, resource_data.length() - sizeof(uint16_t) * 3 /* headers before Key data */)
+ || version_info_key != reinterpret_cast<const unicode16_t*>(root_block->Key))
+ throw_incorrect_version_info();
+
+ //If file has fixed version info
+ if(root_block->ValueLength)
+ {
+ //Get root block value position
+ uint32_t value_pos = get_version_block_value_pos(0, reinterpret_cast<const unicode16_t*>(root_block->Key));
+ //Check value length
+ if(resource_data.length() < value_pos + sizeof(vs_fixedfileinfo))
+ throw_incorrect_version_info();
+
+ //Get VS_FIXEDFILEINFO structure pointer
+ const vs_fixedfileinfo* file_info = reinterpret_cast<const vs_fixedfileinfo*>(resource_data.data() + value_pos);
+ //Check its signature and some other fields
+ if(file_info->dwSignature != vs_ffi_signature || file_info->dwStrucVersion != vs_ffi_strucversion) //Don't check if file_info->dwFileFlagsMask == VS_FFI_FILEFLAGSMASK
+ throw_incorrect_version_info();
+
+ //Save fixed version info
+ ret = file_version_info(*file_info);
+ }
+
+ //Iterate over child elements of VS_VERSIONINFO (StringFileInfo or VarFileInfo)
+ for(uint32_t child_pos = get_version_block_first_child_pos(0, root_block->ValueLength, reinterpret_cast<const unicode16_t*>(root_block->Key));
+ child_pos < root_block->Length;)
+ {
+ //Check block position
+ if(!pe_utils::is_sum_safe(child_pos, sizeof(version_info_block))
+ || resource_data.length() < child_pos + sizeof(version_info_block))
+ throw_incorrect_version_info();
+
+ //Get VERSION_INFO_BLOCK structure pointer
+ const version_info_block* block = reinterpret_cast<const version_info_block*>(resource_data.data() + child_pos);
+
+ //Check its length
+ if(block->Length == 0)
+ throw_incorrect_version_info();
+
+ //Check block key for null-termination
+ if(!pe_utils::is_null_terminated(block->Key, resource_data.length() - child_pos - sizeof(uint16_t) * 3 /* headers before Key data */))
+ throw_incorrect_version_info();
+
+ u16string info_type(reinterpret_cast<const unicode16_t*>(block->Key));
+ //If we encountered StringFileInfo...
+ if(info_type == StringFileInfo)
+ {
+ //Enumerate all string tables
+ for(uint32_t string_table_pos = get_version_block_first_child_pos(child_pos, block->ValueLength, reinterpret_cast<const unicode16_t*>(block->Key));
+ string_table_pos - child_pos < block->Length;)
+ {
+ //Check string table block position
+ if(resource_data.length() < string_table_pos + sizeof(version_info_block))
+ throw_incorrect_version_info();
+
+ //Get VERSION_INFO_BLOCK structure pointer for string table
+ const version_info_block* string_table = reinterpret_cast<const version_info_block*>(resource_data.data() + string_table_pos);
+
+ //Check its length
+ if(string_table->Length == 0)
+ throw_incorrect_version_info();
+
+ //Check string table key for null-termination
+ if(!pe_utils::is_null_terminated(string_table->Key, resource_data.length() - string_table_pos - sizeof(uint16_t) * 3 /* headers before Key data */))
+ throw_incorrect_version_info();
+
+ string_values_map new_values;
+
+ //Enumerate all strings in the string table
+ for(uint32_t string_pos = get_version_block_first_child_pos(string_table_pos, string_table->ValueLength, reinterpret_cast<const unicode16_t*>(string_table->Key));
+ string_pos - string_table_pos < string_table->Length;)
+ {
+ //Check string block position
+ if(resource_data.length() < string_pos + sizeof(version_info_block))
+ throw_incorrect_version_info();
+
+ //Get VERSION_INFO_BLOCK structure pointer for string block
+ const version_info_block* string_block = reinterpret_cast<const version_info_block*>(resource_data.data() + string_pos);
+
+ //Check its length
+ if(string_block->Length == 0)
+ throw_incorrect_version_info();
+
+ //Check string block key for null-termination
+ if(!pe_utils::is_null_terminated(string_block->Key, resource_data.length() - string_pos - sizeof(uint16_t) * 3 /* headers before Key data */))
+ throw_incorrect_version_info();
+
+ u16string data;
+ //If string block has value
+ if(string_block->ValueLength != 0)
+ {
+ //Get value position
+ uint32_t value_pos = get_version_block_value_pos(string_pos, reinterpret_cast<const unicode16_t*>(string_block->Key));
+ //Check it
+ if(resource_data.length() < value_pos + string_block->ValueLength)
+ throw pe_exception("Incorrect resource version info", pe_exception::resource_incorrect_version_info);
+
+ //Get UNICODE string value
+ data = u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + value_pos), string_block->ValueLength);
+ pe_utils::strip_nullbytes(data);
+ }
+
+ //Save name-value pair
+#ifdef PE_BLISS_WINDOWS
+ new_values.insert(std::make_pair(reinterpret_cast<const unicode16_t*>(string_block->Key), data));
+#else
+ new_values.insert(std::make_pair(pe_utils::from_ucs2(reinterpret_cast<const unicode16_t*>(string_block->Key)),
+ pe_utils::from_ucs2(data)));
+#endif
+
+ //Navigate to next string block
+ string_pos += pe_utils::align_up(string_block->Length, sizeof(uint32_t));
+ }
+
+#ifdef PE_BLISS_WINDOWS
+ string_values.insert(std::make_pair(reinterpret_cast<const unicode16_t*>(string_table->Key), new_values));
+#else
+ string_values.insert(std::make_pair(pe_utils::from_ucs2(reinterpret_cast<const unicode16_t*>(string_table->Key)), new_values));
+#endif
+
+ //Navigate to next string table block
+ string_table_pos += pe_utils::align_up(string_table->Length, sizeof(uint32_t));
+ }
+ }
+ else if(info_type == VarFileInfo) //If we encountered VarFileInfo
+ {
+ for(uint32_t var_table_pos = get_version_block_first_child_pos(child_pos, block->ValueLength, reinterpret_cast<const unicode16_t*>(block->Key));
+ var_table_pos - child_pos < block->Length;)
+ {
+ //Check var block position
+ if(resource_data.length() < var_table_pos + sizeof(version_info_block))
+ throw_incorrect_version_info();
+
+ //Get VERSION_INFO_BLOCK structure pointer for var block
+ const version_info_block* var_table = reinterpret_cast<const version_info_block*>(resource_data.data() + var_table_pos);
+
+ //Check its length
+ if(var_table->Length == 0)
+ throw_incorrect_version_info();
+
+ //Check its key for null-termination
+ if(!pe_utils::is_null_terminated(var_table->Key, resource_data.length() - var_table_pos - sizeof(uint16_t) * 3 /* headers before Key data */))
+ throw_incorrect_version_info();
+
+ //If block is "Translation" (actually, there's no other types possible in VarFileInfo) and it has value
+ if(u16string(reinterpret_cast<const unicode16_t*>(var_table->Key)) == Translation && var_table->ValueLength)
+ {
+ //Get its value position
+ uint32_t value_pos = get_version_block_value_pos(var_table_pos, reinterpret_cast<const unicode16_t*>(var_table->Key));
+ //Cherck value length
+ if(resource_data.length() < value_pos + var_table->ValueLength)
+ throw_incorrect_version_info();
+
+ //Get list of translations: pairs of LANGUAGE_ID - CODEPAGE_ID
+ for(unsigned long i = 0; i < var_table->ValueLength; i += sizeof(uint16_t) * 2)
+ {
+ //Pair of WORDs
+ uint16_t lang_id = *reinterpret_cast<const uint16_t*>(resource_data.data() + value_pos + i);
+ uint16_t codepage_id = *reinterpret_cast<const uint16_t*>(resource_data.data() + value_pos + sizeof(uint16_t) + i);
+ //Save translation
+ translations.insert(std::make_pair(lang_id, codepage_id));
+ }
+ }
+
+ //Navigate to next var block
+ var_table_pos += pe_utils::align_up(var_table->Length, sizeof(uint32_t));
+ }
+ }
+ else
+ {
+ throw_incorrect_version_info();
+ }
+
+ //Navigate to next element in root block
+ child_pos += pe_utils::align_up(block->Length, sizeof(uint32_t));
+ }
+
+ return ret;
+}
+
+//Returns full version information:
+//file_version info: versions and file info
+//lang_string_values_map: map of version info strings with encodings
+//translation_values_map: map of translations
+const file_version_info resource_version_info_reader::get_version_info_by_lang(lang_string_values_map& string_values, translation_values_map& translations, uint32_t language) const
+{
+ const std::string& resource_data = res_.get_root_directory() //Type directory
+ .entry_by_id(pe_resource_viewer::resource_version)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_id(1)
+ .get_resource_directory() //Language directory
+ .entry_by_id(language)
+ .get_data_entry() //Data directory
+ .get_data();
+
+ return get_version_info(string_values, translations, resource_data);
+}
+
+//Returns full version information:
+//file_version_info: versions and file info
+//lang_string_values_map: map of version info strings with encodings
+//translation_values_map: map of translations
+const file_version_info resource_version_info_reader::get_version_info(lang_string_values_map& string_values, translation_values_map& translations, uint32_t index) const
+{
+ const resource_directory::entry_list& entries = res_.get_root_directory() //Type directory
+ .entry_by_id(pe_resource_viewer::resource_version)
+ .get_resource_directory() //Name/ID directory
+ .entry_by_id(1)
+ .get_resource_directory() //Language directory
+ .get_entry_list();
+
+ if(entries.size() <= index)
+ throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found);
+
+ return get_version_info(string_values, translations, entries.at(index).get_data_entry().get_data()); //Data directory
+}
+}
diff --git a/tools/pe_bliss/resource_version_info_reader.h b/tools/pe_bliss/resource_version_info_reader.h
new file mode 100644
index 0000000000..c1dfbffdc2
--- /dev/null
+++ b/tools/pe_bliss/resource_version_info_reader.h
@@ -0,0 +1,67 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <map>
+#include "file_version_info.h"
+#include "pe_structures.h"
+#include "version_info_types.h"
+
+namespace pe_bliss
+{
+class pe_resource_viewer;
+
+class resource_version_info_reader
+{
+public: //VERSION INFO
+ resource_version_info_reader(const pe_resource_viewer& res);
+
+ //Returns full version information:
+ //file_version_info: versions and file info
+ //lang_lang_string_values_map: map of version info strings with encodings with encodings
+ //translation_values_map: map of translations
+ const file_version_info get_version_info(lang_string_values_map& string_values, translation_values_map& translations, uint32_t index = 0) const;
+ const file_version_info get_version_info_by_lang(lang_string_values_map& string_values, translation_values_map& translations, uint32_t language) const;
+
+public:
+ //L"VS_VERSION_INFO" key of root version info block
+ static const u16string version_info_key;
+
+private:
+ const pe_resource_viewer& res_;
+
+ //VERSION INFO helpers
+ //Returns aligned version block value position
+ static uint32_t get_version_block_value_pos(uint32_t base_pos, const unicode16_t* key);
+
+ //Returns aligned version block first child position
+ static uint32_t get_version_block_first_child_pos(uint32_t base_pos, uint32_t value_length, const unicode16_t* key);
+
+ //Returns full version information:
+ //file_version_info: versions and file info
+ //lang_string_values_map: map of version info strings with encodings
+ //translation_values_map: map of translations
+ const file_version_info get_version_info(lang_string_values_map& string_values, translation_values_map& translations, const std::string& resource_data) const;
+
+ //Throws an exception (id = resource_incorrect_version_info)
+ static void throw_incorrect_version_info();
+};
+}
diff --git a/tools/pe_bliss/resource_version_info_writer.cpp b/tools/pe_bliss/resource_version_info_writer.cpp
new file mode 100644
index 0000000000..ed95a0f7ea
--- /dev/null
+++ b/tools/pe_bliss/resource_version_info_writer.cpp
@@ -0,0 +1,283 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <string.h>
+#include "resource_version_info_writer.h"
+#include "pe_structures.h"
+#include "resource_internal.h"
+#include "utils.h"
+#include "pe_resource_manager.h"
+#include "resource_version_info_reader.h"
+
+namespace pe_bliss
+{
+using namespace pe_win;
+
+resource_version_info_writer::resource_version_info_writer(pe_resource_manager& res)
+ :res_(res)
+{}
+
+//Sets/replaces full version information:
+//file_version_info: versions and file info
+//lang_string_values_map: map of version info strings with encodings
+//translation_values_map: map of translations
+void resource_version_info_writer::set_version_info(const file_version_info& file_info,
+ const lang_string_values_map& string_values,
+ const translation_values_map& translations,
+ uint32_t language,
+ uint32_t codepage,
+ uint32_t timestamp)
+{
+ std::string version_data;
+
+ //Calculate total size of version resource data
+ uint32_t total_version_info_length =
+ static_cast<uint32_t>(sizeof(version_info_block) - sizeof(uint16_t) + sizeof(uint16_t) /* pading */
+ + (resource_version_info_reader::version_info_key.length() + 1) * 2
+ + sizeof(vs_fixedfileinfo));
+
+ //If we have any strings values
+ if(!string_values.empty())
+ {
+ total_version_info_length += sizeof(version_info_block) - sizeof(uint16_t); //StringFileInfo block
+ total_version_info_length += SizeofStringFileInfo; //Name of block (key)
+
+ //Add required size for version strings
+ for(lang_string_values_map::const_iterator table_it = string_values.begin(); table_it != string_values.end(); ++table_it)
+ {
+ total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 + ((*table_it).first.length() + 1) * 2), sizeof(uint32_t)); //Name of child block and block size (key of string table block)
+
+ const string_values_map& values = (*table_it).second;
+ for(string_values_map::const_iterator it = values.begin(); it != values.end(); ++it)
+ {
+ total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 + ((*it).first.length() + 1) * 2), sizeof(uint32_t));
+ total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(((*it).second.length() + 1) * 2), sizeof(uint32_t));
+ }
+ }
+ }
+
+ //If we have translations
+ if(!translations.empty())
+ {
+ total_version_info_length += (sizeof(version_info_block) - sizeof(uint16_t)) * 2; //VarFileInfo and Translation blocks
+ total_version_info_length += SizeofVarFileInfoAligned; //DWORD-aligned VarFileInfo block name
+ total_version_info_length += SizeofTranslationAligned; //DWORD-aligned Translation block name
+ total_version_info_length += static_cast<uint32_t>(translations.size() * sizeof(uint16_t) * 2);
+ }
+
+ //Resize version data buffer
+ version_data.resize(total_version_info_length);
+
+ //Create root version block
+ version_info_block root_block = {0};
+ root_block.ValueLength = sizeof(vs_fixedfileinfo);
+ root_block.Length = static_cast<uint16_t>(total_version_info_length);
+
+ //Fill fixed file info
+ vs_fixedfileinfo fixed_info = {0};
+ fixed_info.dwFileDateLS = file_info.get_file_date_ls();
+ fixed_info.dwFileDateMS = file_info.get_file_date_ms();
+ fixed_info.dwFileFlags = file_info.get_file_flags();
+ fixed_info.dwFileFlagsMask = vs_ffi_fileflagsmask;
+ fixed_info.dwFileOS = file_info.get_file_os_raw();
+ fixed_info.dwFileSubtype = file_info.get_file_subtype();
+ fixed_info.dwFileType = file_info.get_file_type_raw();
+ fixed_info.dwFileVersionLS = file_info.get_file_version_ls();
+ fixed_info.dwFileVersionMS = file_info.get_file_version_ms();
+ fixed_info.dwSignature = vs_ffi_signature;
+ fixed_info.dwStrucVersion = vs_ffi_strucversion;
+ fixed_info.dwProductVersionLS = file_info.get_product_version_ls();
+ fixed_info.dwProductVersionMS = file_info.get_product_version_ms();
+
+ //Write root block and fixed file info to buffer
+ uint32_t data_ptr = 0;
+ memcpy(&version_data[data_ptr], &root_block, sizeof(version_info_block) - sizeof(uint16_t));
+ data_ptr += sizeof(version_info_block) - sizeof(uint16_t);
+ memcpy(&version_data[data_ptr], resource_version_info_reader::version_info_key.c_str(), (resource_version_info_reader::version_info_key.length() + 1) * sizeof(uint16_t));
+ data_ptr += static_cast<uint32_t>((resource_version_info_reader::version_info_key.length() + 1) * sizeof(uint16_t));
+ memset(&version_data[data_ptr], 0, sizeof(uint16_t));
+ data_ptr += sizeof(uint16_t);
+ memcpy(&version_data[data_ptr], &fixed_info, sizeof(fixed_info));
+ data_ptr += sizeof(fixed_info);
+
+ //Write string values, if any
+ if(!string_values.empty())
+ {
+ //Create string file info root block
+ version_info_block string_file_info_block = {0};
+ string_file_info_block.Type = 1; //Block type is string
+ memcpy(&version_data[data_ptr], &string_file_info_block, sizeof(version_info_block) - sizeof(uint16_t));
+ //We will calculate its length later
+ version_info_block* string_file_info_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]);
+ data_ptr += sizeof(version_info_block) - sizeof(uint16_t);
+
+ uint32_t old_ptr1 = data_ptr; //Used to calculate string file info block length later
+ memcpy(&version_data[data_ptr], StringFileInfo, SizeofStringFileInfo); //Write block name
+ data_ptr += SizeofStringFileInfo;
+
+ //Create string table root block (child of string file info)
+ version_info_block string_table_block = {0};
+ string_table_block.Type = 1; //Block type is string
+
+ for(lang_string_values_map::const_iterator table_it = string_values.begin(); table_it != string_values.end(); ++table_it)
+ {
+ const string_values_map& values = (*table_it).second;
+
+ memcpy(&version_data[data_ptr], &string_table_block, sizeof(version_info_block) - sizeof(uint16_t));
+ //We will calculate its length later
+ version_info_block* string_table_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]);
+ data_ptr += sizeof(version_info_block) - sizeof(uint16_t);
+
+ uint32_t old_ptr2 = data_ptr; //Used to calculate string table block length later
+ uint32_t lang_key_length = static_cast<uint32_t>(((*table_it).first.length() + 1) * sizeof(uint16_t));
+
+#ifdef PE_BLISS_WINDOWS
+ memcpy(&version_data[data_ptr], (*table_it).first.c_str(), lang_key_length); //Write block key
+#else
+ {
+ u16string str(pe_utils::to_ucs2((*table_it).first));
+ memcpy(&version_data[data_ptr], str.c_str(), lang_key_length); //Write block key
+ }
+#endif
+
+ data_ptr += lang_key_length;
+ //Align key if necessary
+ if((sizeof(uint16_t) * 3 + lang_key_length) % sizeof(uint32_t))
+ {
+ memset(&version_data[data_ptr], 0, sizeof(uint16_t));
+ data_ptr += sizeof(uint16_t);
+ }
+
+ //Create string block (child of string table block)
+ version_info_block string_block = {0};
+ string_block.Type = 1; //Block type is string
+ for(string_values_map::const_iterator it = values.begin(); it != values.end(); ++it)
+ {
+ //Calculate value length and key length of string block
+ string_block.ValueLength = static_cast<uint16_t>((*it).second.length() + 1);
+ uint32_t key_length = static_cast<uint32_t>(((*it).first.length() + 1) * sizeof(uint16_t));
+ //Calculate length of block
+ string_block.Length = static_cast<uint16_t>(pe_utils::align_up(sizeof(uint16_t) * 3 + key_length, sizeof(uint32_t)) + string_block.ValueLength * sizeof(uint16_t));
+
+ //Write string block
+ memcpy(&version_data[data_ptr], &string_block, sizeof(version_info_block) - sizeof(uint16_t));
+ data_ptr += sizeof(version_info_block) - sizeof(uint16_t);
+
+#ifdef PE_BLISS_WINDOWS
+ memcpy(&version_data[data_ptr], (*it).first.c_str(), key_length); //Write block key
+#else
+ {
+ u16string str(pe_utils::to_ucs2((*it).first));
+ memcpy(&version_data[data_ptr], str.c_str(), key_length); //Write block key
+ }
+#endif
+
+ data_ptr += key_length;
+ //Align key if necessary
+ if((sizeof(uint16_t) * 3 + key_length) % sizeof(uint32_t))
+ {
+ memset(&version_data[data_ptr], 0, sizeof(uint16_t));
+ data_ptr += sizeof(uint16_t);
+ }
+
+ //Write block data (value)
+#ifdef PE_BLISS_WINDOWS
+ memcpy(&version_data[data_ptr], (*it).second.c_str(), string_block.ValueLength * sizeof(uint16_t));
+#else
+ {
+ u16string str(pe_utils::to_ucs2((*it).second));
+ memcpy(&version_data[data_ptr], str.c_str(), string_block.ValueLength * sizeof(uint16_t));
+ }
+#endif
+
+ data_ptr += string_block.ValueLength * 2;
+ //Align data if necessary
+ if((string_block.ValueLength * 2) % sizeof(uint32_t))
+ {
+ memset(&version_data[data_ptr], 0, sizeof(uint16_t));
+ data_ptr += sizeof(uint16_t);
+ }
+ }
+
+ //Calculate string table and string file info blocks lengths
+ string_table_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr2 + sizeof(uint16_t) * 3);
+ }
+
+ string_file_info_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr1 + sizeof(uint16_t) * 3);
+ }
+
+ //If we have transactions
+ if(!translations.empty())
+ {
+ //Create root var file info block
+ version_info_block var_file_info_block = {0};
+ var_file_info_block.Type = 1; //Type of block is string
+ //Write block header
+ memcpy(&version_data[data_ptr], &var_file_info_block, sizeof(version_info_block) - sizeof(uint16_t));
+ //We will calculate its length later
+ version_info_block* var_file_info_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]);
+ data_ptr += sizeof(version_info_block) - sizeof(uint16_t);
+
+ uint32_t old_ptr1 = data_ptr; //Used to calculate var file info block length later
+ memcpy(&version_data[data_ptr], VarFileInfoAligned, SizeofVarFileInfoAligned); //Write block key (aligned)
+ data_ptr += SizeofVarFileInfoAligned;
+
+ //Create root translation block (child of var file info block)
+ version_info_block translation_block = {0};
+ //Write block header
+ memcpy(&version_data[data_ptr], &translation_block, sizeof(version_info_block) - sizeof(uint16_t));
+ //We will calculate its length later
+ version_info_block* translation_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]);
+ data_ptr += sizeof(version_info_block) - sizeof(uint16_t);
+
+ uint32_t old_ptr2 = data_ptr; //Used to calculate var file info block length later
+ memcpy(&version_data[data_ptr], TranslationAligned, SizeofTranslationAligned); //Write block key (aligned)
+ data_ptr += SizeofTranslationAligned;
+
+ //Calculate translation block value length
+ translation_block_ptr->ValueLength = static_cast<uint16_t>(sizeof(uint16_t) * 2 * translations.size());
+
+ //Write translation values to block
+ for(translation_values_map::const_iterator it = translations.begin(); it != translations.end(); ++it)
+ {
+ uint16_t lang_id = (*it).first; //Language ID
+ uint16_t codepage_id = (*it).second; //Codepage ID
+ memcpy(&version_data[data_ptr], &lang_id, sizeof(lang_id));
+ data_ptr += sizeof(lang_id);
+ memcpy(&version_data[data_ptr], &codepage_id, sizeof(codepage_id));
+ data_ptr += sizeof(codepage_id);
+ }
+
+ //Calculate Translation and VarFileInfo blocks lengths
+ translation_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr2 + sizeof(uint16_t) * 3);
+ var_file_info_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr1 + sizeof(uint16_t) * 3);
+ }
+
+ //Add/replace version info resource
+ res_.add_resource(version_data, pe_resource_viewer::resource_version, 1, language, codepage, timestamp);
+}
+
+//Removes version info by language (ID = 1)
+bool resource_version_info_writer::remove_version_info(uint32_t language)
+{
+ return res_.remove_resource(pe_resource_viewer::resource_version, 1, language);
+}
+}
diff --git a/tools/pe_bliss/resource_version_info_writer.h b/tools/pe_bliss/resource_version_info_writer.h
new file mode 100644
index 0000000000..da279ddedb
--- /dev/null
+++ b/tools/pe_bliss/resource_version_info_writer.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include "version_info_types.h"
+#include "file_version_info.h"
+
+namespace pe_bliss
+{
+class pe_resource_manager;
+
+class resource_version_info_writer
+{
+public:
+ resource_version_info_writer(pe_resource_manager& res);
+
+ //Sets/replaces full version information:
+ //file_version_info: versions and file info
+ //lang_string_values_map: map of version info strings with encodings
+ //translation_values_map: map of translations
+ void set_version_info(const file_version_info& file_info,
+ const lang_string_values_map& string_values,
+ const translation_values_map& translations,
+ uint32_t language,
+ uint32_t codepage = 0,
+ uint32_t timestamp = 0);
+
+ //Removes version info by language (ID = 1)
+ bool remove_version_info(uint32_t language);
+
+private:
+ pe_resource_manager& res_;
+};
+}
diff --git a/tools/pe_bliss/stdint_defs.h b/tools/pe_bliss/stdint_defs.h
new file mode 100644
index 0000000000..bbc003690a
--- /dev/null
+++ b/tools/pe_bliss/stdint_defs.h
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#ifdef _MSC_VER
+#if _MSC_VER < 1600
+namespace pe_bliss
+{
+ //stdint.h definitions for MSVC 2008 and earlier, as
+ //it doesn't have them
+ typedef signed char int8_t;
+ typedef short int16_t;
+ typedef int int32_t;
+
+ typedef unsigned char uint8_t;
+ typedef unsigned short uint16_t;
+ typedef unsigned int uint32_t;
+
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+}
+#else
+#include <stdint.h>
+#endif
+#else
+#include <stdint.h>
+#endif
diff --git a/tools/pe_bliss/utils.cpp b/tools/pe_bliss/utils.cpp
new file mode 100644
index 0000000000..e6a75d5497
--- /dev/null
+++ b/tools/pe_bliss/utils.cpp
@@ -0,0 +1,85 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <string.h>
+#include "utils.h"
+#include "pe_exception.h"
+
+
+namespace pe_bliss
+{
+const double pe_utils::log_2 = 1.44269504088896340736; //instead of using M_LOG2E
+
+//Returns stream size
+std::streamoff pe_utils::get_file_size(std::istream& file)
+{
+ //Get old istream offset
+ std::streamoff old_offset = file.tellg();
+ file.seekg(0, std::ios::end);
+ std::streamoff filesize = file.tellg();
+ //Set old istream offset
+ file.seekg(old_offset);
+ return filesize;
+}
+
+#ifndef PE_BLISS_WINDOWS
+const u16string pe_utils::to_ucs2(const std::wstring& str)
+{
+ u16string ret;
+ if(str.empty())
+ return ret;
+
+ int len = str.length();
+
+ ret.resize(len);
+
+ for(int i=0;i<len;i++) {
+ ret[i]=str[i]&0xFFFF;
+ }
+
+ return ret;
+}
+
+const std::wstring pe_utils::from_ucs2(const u16string& str)
+{
+ std::wstring ret;
+ if(str.empty())
+ return ret;
+
+ int len = str.length();
+ ret.resize(str.length());
+
+ for(int i=0;i<len;i++) {
+ ret[i]=str[i];
+ }
+
+ return ret;
+}
+#endif
+
+bool operator==(const pe_win::guid& guid1, const pe_win::guid& guid2)
+{
+ return guid1.Data1 == guid2.Data1
+ && guid1.Data2 == guid2.Data2
+ && guid1.Data3 == guid2.Data3
+ && !memcmp(guid1.Data4, guid2.Data4, sizeof(guid1.Data4));
+}
+}
diff --git a/tools/pe_bliss/utils.h b/tools/pe_bliss/utils.h
new file mode 100644
index 0000000000..29125f8dc1
--- /dev/null
+++ b/tools/pe_bliss/utils.h
@@ -0,0 +1,105 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <istream>
+#include <string>
+#include "stdint_defs.h"
+#include "pe_structures.h"
+
+namespace pe_bliss
+{
+class pe_utils
+{
+public:
+ //Returns true if string "data" with maximum length "raw_length" is null-terminated
+ template<typename T>
+ static bool is_null_terminated(const T* data, size_t raw_length)
+ {
+ raw_length /= sizeof(T);
+ for(size_t l = 0; l < raw_length; l++)
+ {
+ if(data[l] == static_cast<T>(L'\0'))
+ return true;
+ }
+
+ return false;
+ }
+
+ //Helper template function to strip nullbytes in the end of string
+ template<typename T>
+ static void strip_nullbytes(std::basic_string<T>& str)
+ {
+ while(!*(str.end() - 1) && !str.empty())
+ str.erase(str.length() - 1);
+ }
+
+ //Helper function to determine if number is power of 2
+ template<typename T>
+ static inline bool is_power_of_2(T x)
+ {
+ return !(x & (x - 1));
+ }
+
+ //Helper function to align number down
+ template<typename T>
+ static inline T align_down(T x, uint32_t align)
+ {
+ return x & ~(static_cast<T>(align) - 1);
+ }
+
+ //Helper function to align number up
+ template<typename T>
+ static inline T align_up(T x, uint32_t align)
+ {
+ return (x & static_cast<T>(align - 1)) ? align_down(x, align) + static_cast<T>(align) : x;
+ }
+
+ //Returns true if sum of two unsigned integers is safe (no overflow occurs)
+ static inline bool is_sum_safe(uint32_t a, uint32_t b)
+ {
+ return a <= static_cast<uint32_t>(-1) - b;
+ }
+
+ //Two gigabytes value in bytes
+ static const uint32_t two_gb = 0x80000000;
+ static const uint32_t max_dword = 0xFFFFFFFF;
+ static const uint32_t max_word = 0x0000FFFF;
+ static const double log_2; //instead of using M_LOG2E
+
+ //Returns stream size
+ static std::streamoff get_file_size(std::istream& file);
+
+#ifndef PE_BLISS_WINDOWS
+public:
+ static const u16string to_ucs2(const std::wstring& str);
+ static const std::wstring from_ucs2(const u16string& str);
+#endif
+
+private:
+ pe_utils();
+ pe_utils(pe_utils&);
+ pe_utils& operator=(const pe_utils&);
+};
+
+//Windows GUID comparison
+bool operator==(const pe_win::guid& guid1, const pe_win::guid& guid2);
+}
diff --git a/tools/pe_bliss/version_info_editor.cpp b/tools/pe_bliss/version_info_editor.cpp
new file mode 100644
index 0000000000..199eebfd54
--- /dev/null
+++ b/tools/pe_bliss/version_info_editor.cpp
@@ -0,0 +1,184 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <sstream>
+#include <iomanip>
+#include "version_info_types.h"
+#include "version_info_editor.h"
+#include "version_info_viewer.h"
+
+namespace pe_bliss
+{
+//Default constructor
+//strings - version info strings with charsets
+//translations - version info translations map
+version_info_editor::version_info_editor(lang_string_values_map& strings, translation_values_map& translations)
+ :version_info_viewer(strings, translations),
+ strings_edit_(strings),
+ translations_edit_(translations)
+{}
+
+//Below functions have parameter translation
+//If it's empty, the default language translation will be taken
+//If there's no default language translation, the first one will be taken
+
+//Sets company name
+void version_info_editor::set_company_name(const std::wstring& value, const std::wstring& translation)
+{
+ set_property(L"CompanyName", value, translation);
+}
+
+//Sets file description
+void version_info_editor::set_file_description(const std::wstring& value, const std::wstring& translation)
+{
+ set_property(L"FileDescription", value, translation);
+}
+
+//Sets file version
+void version_info_editor::set_file_version(const std::wstring& value, const std::wstring& translation)
+{
+ set_property(L"FileVersion", value, translation);
+}
+
+//Sets internal file name
+void version_info_editor::set_internal_name(const std::wstring& value, const std::wstring& translation)
+{
+ set_property(L"InternalName", value, translation);
+}
+
+//Sets legal copyright
+void version_info_editor::set_legal_copyright(const std::wstring& value, const std::wstring& translation)
+{
+ set_property(L"LegalCopyright", value, translation);
+}
+
+//Sets original file name
+void version_info_editor::set_original_filename(const std::wstring& value, const std::wstring& translation)
+{
+ set_property(L"OriginalFilename", value, translation);
+}
+
+//Sets product name
+void version_info_editor::set_product_name(const std::wstring& value, const std::wstring& translation)
+{
+ set_property(L"ProductName", value, translation);
+}
+
+//Sets product version
+void version_info_editor::set_product_version(const std::wstring& value, const std::wstring& translation)
+{
+ set_property(L"ProductVersion", value, translation);
+}
+
+//Sets version info property value
+//property_name - property name
+//value - property value
+//If translation does not exist, it will be added
+//If property does not exist, it will be added
+void version_info_editor::set_property(const std::wstring& property_name, const std::wstring& value, const std::wstring& translation)
+{
+ lang_string_values_map::iterator it = strings_edit_.begin();
+
+ if(translation.empty())
+ {
+ //If no translation was specified
+ it = strings_edit_.find(default_language_translation); //Find default translation table
+ if(it == strings_edit_.end()) //If there's no default translation table, take the first one
+ {
+ it = strings_edit_.begin();
+ if(it == strings_edit_.end()) //If there's no any translation table, add default one
+ {
+ it = strings_edit_.insert(std::make_pair(default_language_translation, string_values_map())).first;
+ //Also add it to translations list
+ add_translation(default_language_translation);
+ }
+ }
+ }
+ else
+ {
+ it = strings_edit_.find(translation); //Find specified translation table
+ if(it == strings_edit_.end()) //If there's no translation, add it
+ {
+ it = strings_edit_.insert(std::make_pair(translation, string_values_map())).first;
+ //Also add it to translations list
+ add_translation(translation);
+ }
+ }
+
+ //Change value of the required property
+ ((*it).second)[property_name] = value;
+}
+
+//Adds translation to translation list
+void version_info_editor::add_translation(const std::wstring& translation)
+{
+ std::pair<uint16_t, uint16_t> translation_ids(translation_from_string(translation));
+ add_translation(translation_ids.first, translation_ids.second);
+}
+
+void version_info_editor::add_translation(uint16_t language_id, uint16_t codepage_id)
+{
+ std::pair<translation_values_map::const_iterator, translation_values_map::const_iterator>
+ range(translations_edit_.equal_range(language_id));
+
+ //If translation already exists
+ for(translation_values_map::const_iterator it = range.first; it != range.second; ++it)
+ {
+ if((*it).second == codepage_id)
+ return;
+ }
+
+ translations_edit_.insert(std::make_pair(language_id, codepage_id));
+}
+
+//Removes translation from translations and strings lists
+void version_info_editor::remove_translation(const std::wstring& translation)
+{
+ std::pair<uint16_t, uint16_t> translation_ids(translation_from_string(translation));
+ remove_translation(translation_ids.first, translation_ids.second);
+}
+
+void version_info_editor::remove_translation(uint16_t language_id, uint16_t codepage_id)
+{
+ {
+ //Erase string table (if exists)
+ std::wstringstream ss;
+ ss << std::hex
+ << std::setw(4) << std::setfill(L'0') << language_id
+ << std::setw(4) << std::setfill(L'0') << codepage_id;
+
+ strings_edit_.erase(ss.str());
+ }
+
+ //Find and erase translation from translations table
+ std::pair<translation_values_map::iterator, translation_values_map::iterator>
+ it_pair = translations_edit_.equal_range(language_id);
+
+ for(translation_values_map::iterator it = it_pair.first; it != it_pair.second; ++it)
+ {
+ if((*it).second == codepage_id)
+ {
+ translations_edit_.erase(it);
+ break;
+ }
+ }
+}
+}
diff --git a/tools/pe_bliss/version_info_editor.h b/tools/pe_bliss/version_info_editor.h
new file mode 100644
index 0000000000..53d3dc62c1
--- /dev/null
+++ b/tools/pe_bliss/version_info_editor.h
@@ -0,0 +1,79 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include "version_info_types.h"
+#include "version_info_viewer.h"
+
+namespace pe_bliss
+{
+ //Helper class to read and edit version information
+ //lang_string_values_map: map of version info strings with encodings
+ //translation_values_map: map of translations
+ class version_info_editor : public version_info_viewer
+ {
+ public:
+ //Default constructor
+ //strings - version info strings with charsets
+ //translations - version info translations map
+ version_info_editor(lang_string_values_map& strings, translation_values_map& translations);
+
+ //Below functions have parameter translation
+ //If it's empty, the default language translation will be taken
+ //If there's no default language translation, the first one will be taken
+
+ //Sets company name
+ void set_company_name(const std::wstring& value, const std::wstring& translation = std::wstring());
+ //Sets file description
+ void set_file_description(const std::wstring& value, const std::wstring& translation = std::wstring());
+ //Sets file version
+ void set_file_version(const std::wstring& value, const std::wstring& translation = std::wstring());
+ //Sets internal file name
+ void set_internal_name(const std::wstring& value, const std::wstring& translation = std::wstring());
+ //Sets legal copyright
+ void set_legal_copyright(const std::wstring& value, const std::wstring& translation = std::wstring());
+ //Sets original file name
+ void set_original_filename(const std::wstring& value, const std::wstring& translation = std::wstring());
+ //Sets product name
+ void set_product_name(const std::wstring& value, const std::wstring& translation = std::wstring());
+ //Sets product version
+ void set_product_version(const std::wstring& value, const std::wstring& translation = std::wstring());
+
+ //Sets version info property value
+ //property_name - property name
+ //value - property value
+ //If translation does not exist, it will be added to strings and translations lists
+ //If property does not exist, it will be added
+ void set_property(const std::wstring& property_name, const std::wstring& value, const std::wstring& translation = std::wstring());
+
+ //Adds translation to translation list
+ void add_translation(const std::wstring& translation);
+ void add_translation(uint16_t language_id, uint16_t codepage_id);
+
+ //Removes translation from translations and strings lists
+ void remove_translation(const std::wstring& translation);
+ void remove_translation(uint16_t language_id, uint16_t codepage_id);
+
+ private:
+ lang_string_values_map& strings_edit_;
+ translation_values_map& translations_edit_;
+ };
+}
diff --git a/tools/pe_bliss/version_info_types.h b/tools/pe_bliss/version_info_types.h
new file mode 100644
index 0000000000..6010c9691e
--- /dev/null
+++ b/tools/pe_bliss/version_info_types.h
@@ -0,0 +1,38 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <map>
+#include <string>
+#include "stdint_defs.h"
+
+namespace pe_bliss
+{
+ //Typedef for version info functions: Name - Value
+ typedef std::map<std::wstring, std::wstring> string_values_map;
+ //Typedef for version info functions: Language string - String Values Map
+ //Language String consists of LangID and CharsetID
+ //E.g. 041904b0 for Russian UNICODE, 040004b0 for Process Default Language UNICODE
+ typedef std::map<std::wstring, string_values_map> lang_string_values_map;
+
+ //Typedef for version info functions: Language - Character Set
+ typedef std::multimap<uint16_t, uint16_t> translation_values_map;
+}
diff --git a/tools/pe_bliss/version_info_viewer.cpp b/tools/pe_bliss/version_info_viewer.cpp
new file mode 100644
index 0000000000..6e2d0d5c5b
--- /dev/null
+++ b/tools/pe_bliss/version_info_viewer.cpp
@@ -0,0 +1,180 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include <iomanip>
+#include <sstream>
+#include "pe_exception.h"
+#include "version_info_viewer.h"
+
+namespace pe_bliss
+{
+//Default process language, UNICODE
+const std::wstring version_info_viewer::default_language_translation(L"040904b0");
+
+//Default constructor
+//strings - version info strings with charsets
+//translations - version info translations map
+version_info_viewer::version_info_viewer(const lang_string_values_map& strings, const translation_values_map& translations)
+ :strings_(strings), translations_(translations)
+{}
+
+//Below functions have parameter translation
+//If it's empty, the default language translation will be taken
+//If there's no default language translation, the first one will be taken
+
+//Returns company name
+const std::wstring version_info_viewer::get_company_name(const std::wstring& translation) const
+{
+ return get_property(L"CompanyName", translation);
+}
+
+//Returns file description
+const std::wstring version_info_viewer::get_file_description(const std::wstring& translation) const
+{
+ return get_property(L"FileDescription", translation);
+}
+
+//Returns file version
+const std::wstring version_info_viewer::get_file_version(const std::wstring& translation) const
+{
+ return get_property(L"FileVersion", translation);
+}
+
+//Returns internal file name
+const std::wstring version_info_viewer::get_internal_name(const std::wstring& translation) const
+{
+ return get_property(L"InternalName", translation);
+}
+
+//Returns legal copyright
+const std::wstring version_info_viewer::get_legal_copyright(const std::wstring& translation) const
+{
+ return get_property(L"LegalCopyright", translation);
+}
+
+//Returns original file name
+const std::wstring version_info_viewer::get_original_filename(const std::wstring& translation) const
+{
+ return get_property(L"OriginalFilename", translation);
+}
+
+//Returns product name
+const std::wstring version_info_viewer::get_product_name(const std::wstring& translation) const
+{
+ return get_property(L"ProductName", translation);
+}
+
+//Returns product version
+const std::wstring version_info_viewer::get_product_version(const std::wstring& translation) const
+{
+ return get_property(L"ProductVersion", translation);
+}
+
+//Returns list of translations in string representation
+const version_info_viewer::translation_list version_info_viewer::get_translation_list() const
+{
+ translation_list ret;
+
+ //Enumerate all translations
+ for(translation_values_map::const_iterator it = translations_.begin(); it != translations_.end(); ++it)
+ {
+ //Create string representation of translation value
+ std::wstringstream ss;
+ ss << std::hex
+ << std::setw(4) << std::setfill(L'0') << (*it).first
+ << std::setw(4) << std::setfill(L'0') << (*it).second;
+
+ //Save it
+ ret.push_back(ss.str());
+ }
+
+ return ret;
+}
+
+//Returns version info property value
+//property_name - required property name
+//If throw_if_absent = true, will throw exception if property does not exist
+//If throw_if_absent = false, will return empty string if property does not exist
+const std::wstring version_info_viewer::get_property(const std::wstring& property_name, const std::wstring& translation, bool throw_if_absent) const
+{
+ std::wstring ret;
+
+ //If there're no strings
+ if(strings_.empty())
+ {
+ if(throw_if_absent)
+ throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist);
+
+ return ret;
+ }
+
+ lang_string_values_map::const_iterator it = strings_.begin();
+
+ if(translation.empty())
+ {
+ //If no translation was specified
+ it = strings_.find(default_language_translation); //Find default translation table
+ if(it == strings_.end()) //If there's no default translation table, take the first one
+ it = strings_.begin();
+ }
+ else
+ {
+ it = strings_.find(translation); //Find specified translation table
+ if(it == strings_.end())
+ {
+ if(throw_if_absent)
+ throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist);
+
+ return ret;
+ }
+ }
+
+ //Find value of the required property
+ string_values_map::const_iterator str_it = (*it).second.find(property_name);
+
+ if(str_it == (*it).second.end())
+ {
+ if(throw_if_absent)
+ throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist);
+
+ return ret;
+ }
+
+ ret = (*str_it).second;
+
+ return ret;
+}
+
+//Converts translation HEX-string to pair of language ID and codepage ID
+const version_info_viewer::translation_pair version_info_viewer::translation_from_string(const std::wstring& translation)
+{
+ uint32_t translation_id = 0;
+
+ {
+ //Convert string to DWORD
+ std::wstringstream ss;
+ ss << std::hex << translation;
+ ss >> translation_id;
+ }
+
+ return std::make_pair(static_cast<uint16_t>(translation_id >> 16), static_cast<uint16_t>(translation_id & 0xFFFF));
+}
+}
diff --git a/tools/pe_bliss/version_info_viewer.h b/tools/pe_bliss/version_info_viewer.h
new file mode 100644
index 0000000000..bc2f6f2ba7
--- /dev/null
+++ b/tools/pe_bliss/version_info_viewer.h
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* Copyright (c) 2015 dx, http://kaimi.ru */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#pragma once
+#include <map>
+#include <vector>
+#include <string>
+#include "pe_resource_viewer.h"
+#include "pe_structures.h"
+#include "version_info_types.h"
+
+namespace pe_bliss
+{
+//Helper class to read version information
+//lang_string_values_map: map of version info strings with encodings
+//translation_values_map: map of translations
+class version_info_viewer
+{
+public:
+ //Useful typedefs
+ typedef std::pair<uint16_t, uint16_t> translation_pair;
+ typedef std::vector<std::wstring> translation_list;
+
+public:
+ //Default constructor
+ //strings - version info strings with charsets
+ //translations - version info translations map
+ version_info_viewer(const lang_string_values_map& strings, const translation_values_map& translations);
+
+ //Below functions have parameter translation
+ //If it's empty, the default language translation will be taken
+ //If there's no default language translation, the first one will be taken
+
+ //Returns company name
+ const std::wstring get_company_name(const std::wstring& translation = std::wstring()) const;
+ //Returns file description
+ const std::wstring get_file_description(const std::wstring& translation = std::wstring()) const;
+ //Returns file version
+ const std::wstring get_file_version(const std::wstring& translation = std::wstring()) const;
+ //Returns internal file name
+ const std::wstring get_internal_name(const std::wstring& translation = std::wstring()) const;
+ //Returns legal copyright
+ const std::wstring get_legal_copyright(const std::wstring& translation = std::wstring()) const;
+ //Returns original file name
+ const std::wstring get_original_filename(const std::wstring& translation = std::wstring()) const;
+ //Returns product name
+ const std::wstring get_product_name(const std::wstring& translation = std::wstring()) const;
+ //Returns product version
+ const std::wstring get_product_version(const std::wstring& translation = std::wstring()) const;
+
+ //Returns list of translations in string representation
+ const translation_list get_translation_list() const;
+
+ //Returns version info property value
+ //property_name - required property name
+ //If throw_if_absent = true, will throw exception if property does not exist
+ //If throw_if_absent = false, will return empty string if property does not exist
+ const std::wstring get_property(const std::wstring& property_name, const std::wstring& translation = std::wstring(), bool throw_if_absent = false) const;
+
+ //Converts translation HEX-string to pair of language ID and codepage ID
+ static const translation_pair translation_from_string(const std::wstring& translation);
+
+public:
+ //Default process language, UNICODE
+ static const std::wstring default_language_translation;
+
+private:
+ const lang_string_values_map& strings_;
+ const translation_values_map& translations_;
+};
+}
diff --git a/tools/script_plugins/time/time.gd b/tools/script_plugins/time/time.gd
index 66b3e9ed04..2e56d89d4f 100644
--- a/tools/script_plugins/time/time.gd
+++ b/tools/script_plugins/time/time.gd
@@ -21,12 +21,12 @@ func _init():
timer.set_one_shot(false)
timer.connect("timeout",self,"_timeout")
-func _enter_scene():
+func _enter_tree():
label = Label.new()
add_custom_control(CONTAINER_TOOLBAR,label)
timer.start()
-func _exit_scene():
+func _exit_tree():
timer.stop()
label.free()
label=null
diff --git a/tools/scripts/file-hex-array.py b/tools/scripts/file-hex-array.py
new file mode 100755
index 0000000000..05352396f1
--- /dev/null
+++ b/tools/scripts/file-hex-array.py
@@ -0,0 +1,52 @@
+import binascii
+import os.path
+import sys
+
+def tof(filepath):
+ with open(filepath, 'r') as f:
+ content = f.read()
+ content = content.replace("0x","")
+ content = content.split(',')
+ for i in range(len(content)):
+ if len(content[i]) == 1: content[i] = "0" + content[i]
+ content = "".join(content)
+ with open(filepath+".file", 'wb') as f:
+ content = f.write(content.decode("hex"))
+ print(os.path.basename(filepath)+".file created.")
+ exit(0)
+
+def toa(filepath):
+ with open(filepath, 'rb') as f:
+ content = f.read()
+ content = binascii.hexlify(content)
+ content = [content[i:i+2] for i in range(0, len(content), 2)]
+ content = ",0x".join(content)
+ content = "0x" + content
+ content = content.replace("0x00","0x0")
+ with open(filepath+".array", 'w') as f:
+ content = f.write(content)
+ print(os.path.basename(filepath)+".array created.")
+ exit(0)
+
+def usage():
+ print("========================================================\n\
+#\n\
+# Usage: python file-hex-array.py [action] [option]\n\
+#\n\
+# Arguments:\n\
+# action ==> toa # convert file to array [option is file path]\n\
+# tof # convert array to file [option is array file path]\n\
+#\n\
+# Example : python file-hex-array.py toa 1.png\n\
+#\n\
+========================================================")
+ exit(1)
+
+if len(sys.argv) != 3:
+ usage()
+if sys.argv[1] == "toa" and os.path.isfile(sys.argv[2]):
+ toa(sys.argv[2])
+elif sys.argv[1] == "tof" and os.path.isfile(sys.argv[2]):
+ tof(sys.argv[2])
+else:
+ usage() \ No newline at end of file
diff --git a/tools/scrits/makeargs.py b/tools/scripts/makeargs.py
index 6f7afa6328..6f7afa6328 100644
--- a/tools/scrits/makeargs.py
+++ b/tools/scripts/makeargs.py