mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 01:39:21 +02:00
Compare commits
38 commits
542c2aa617
...
e4e2e4369e
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e4e2e4369e | ||
![]() |
2e0e64b7c6 | ||
![]() |
cdc5af10e4 | ||
![]() |
c5d0f6925b | ||
![]() |
384d72782a | ||
![]() |
154aa4460c | ||
![]() |
d38f512437 | ||
![]() |
81fce4fed5 | ||
![]() |
3e240563ab | ||
![]() |
4172e448dd | ||
![]() |
21b27795dd | ||
![]() |
a48c081e61 | ||
![]() |
c7b125b1c7 | ||
![]() |
c14f03c79a | ||
![]() |
ed1a1b81f0 | ||
![]() |
bcefa8bf09 | ||
![]() |
105f9ef570 | ||
![]() |
729642cbf6 | ||
![]() |
be0ca420eb | ||
![]() |
3c90216365 | ||
![]() |
ce0c7b9229 | ||
![]() |
b4f7c920e6 | ||
![]() |
6773801f6e | ||
![]() |
c001c4c65d | ||
![]() |
f4ddff1a2c | ||
![]() |
328042f00f | ||
![]() |
42115f6df0 | ||
![]() |
6bc3871e67 | ||
![]() |
16a2e78806 | ||
![]() |
245ba82d8b | ||
![]() |
15ac693e76 | ||
![]() |
3ff52f05b1 | ||
![]() |
42f4b3462e | ||
![]() |
ad32d0177b | ||
![]() |
7b61358488 | ||
![]() |
bb19782c35 | ||
![]() |
9c9938e066 | ||
![]() |
28313c6574 |
242 changed files with 7766 additions and 2909 deletions
|
@ -107,6 +107,9 @@ def isNativeBinaryMakeTask(Task task, String platform) {
|
|||
*
|
||||
******************************************************************************************/
|
||||
def shouldSkipNative(task) {
|
||||
if (rootProject.hasProperty("skipAllNatives")) {
|
||||
return true;
|
||||
}
|
||||
return task.ext.has("skipNative") && task.ext.get("skipNative")
|
||||
}
|
||||
|
||||
|
|
|
@ -28,63 +28,56 @@ dependencies {
|
|||
api project(':Debugger-rmi-trace')
|
||||
}
|
||||
|
||||
ext.tlb = file("build/os/win_x86_64/dbgmodel.tlb")
|
||||
def tlb = file("build/os/win_x86_64/dbgmodel.tlb")
|
||||
def depsFile = file("${DEPS_DIR}/Debugger-agent-dbgeng/dbgmodel.tlb")
|
||||
def binRepoFile = file("${BIN_REPO}/${getGhidraRelativePath(project)}/os/win_x86_64/dbgmodel.tlb")
|
||||
|
||||
if ("win_x86_64".equals(getCurrentPlatformName())) {
|
||||
|
||||
String makeName = "win_x86_64DbgmodelTlbMake"
|
||||
task(type: Exec, makeName) {
|
||||
ext.tmpBatch = file("build/buildTlb.bat")
|
||||
ext.idl = file("src/main/py/src/ghidradbg/dbgmodel/DbgModel.idl")
|
||||
inputs.file(idl)
|
||||
outputs.file(tlb)
|
||||
|
||||
doFirst {
|
||||
file(tlb).parentFile.mkdirs()
|
||||
def midlCmd = "midl /tlb \"${tlb}\" \"${idl}\""
|
||||
println "Executing: " + midlCmd
|
||||
|
||||
tmpBatch.withWriter { out ->
|
||||
out.println "call " + VISUAL_STUDIO_VCVARS_CMD
|
||||
out.println midlCmd
|
||||
}
|
||||
}
|
||||
doLast {
|
||||
assert file(tlb).exists() : "Failed to build dbgmodel.tlb"
|
||||
}
|
||||
|
||||
commandLine "cmd", "/c", tmpBatch
|
||||
}
|
||||
|
||||
tasks.assemblePyPackage {
|
||||
from(tasks."$makeName") {
|
||||
into("src/ghidradbg/dbgmodel/tlb")
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (file(tlb).exists()) {
|
||||
// required for multi-platform build
|
||||
tasks.assemblePyPackage {
|
||||
from(tlb) {
|
||||
println "Copying existing tlb build artifact: " + tlb
|
||||
into("src/ghidradbg/dbgmodel/tlb")
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
def depsFile = file("${DEPS_DIR}/Debugger-agent-dbgeng/dbgmodel.tlb")
|
||||
def binRepoFile = file("${BIN_REPO}/${getGhidraRelativePath(project)}/os/win_x86_64/dbgmodel.tlb")
|
||||
tasks.assemblePyPackage {
|
||||
def prebuiltTlb = depsFile.exists() ? depsFile : (binRepoFile.exists() ? binRepoFile : null)
|
||||
assert prebuiltTlb != null : "Failed to locate prebuilt TLB file"
|
||||
|
||||
if (prebuiltTlb) {
|
||||
tasks.assemblePyPackage {
|
||||
from(prebuiltTlb) {
|
||||
into("src/ghidradbg/dbgmodel/tlb")
|
||||
}
|
||||
from(prebuiltTlb) {
|
||||
into("src/ghidradbg/dbgmodel/tlb")
|
||||
}
|
||||
}
|
||||
|
||||
task buildTlb(type: Exec) {
|
||||
|
||||
def tmpBatch = file("build/buildTlb.bat")
|
||||
def idl = file("src/main/py/src/ghidradbg/dbgmodel/DbgModel.idl")
|
||||
|
||||
inputs.file(idl)
|
||||
outputs.file(tlb)
|
||||
|
||||
doFirst {
|
||||
assert isCurrentX86_64() && isCurrentWindows() : "Can only build TLB on Windows x86"
|
||||
file(tlb).parentFile.mkdirs()
|
||||
def midlCmd = "midl /tlb \"${tlb}\" \"${idl}\""
|
||||
println "Executing: " + midlCmd
|
||||
|
||||
tmpBatch.withWriter { out ->
|
||||
out.println "call " + VISUAL_STUDIO_VCVARS_CMD
|
||||
out.println midlCmd
|
||||
}
|
||||
}
|
||||
else {
|
||||
println "****WARNING:**** dbgmodel.tlb omitted from ghidradbg python package"
|
||||
|
||||
doLast {
|
||||
assert file(tlb).exists() : "Failed to build dbgmodel.tlb"
|
||||
}
|
||||
|
||||
commandLine "cmd", "/c", tmpBatch
|
||||
}
|
||||
|
||||
task prebuildTlb(type: Copy) {
|
||||
|
||||
dependsOn buildTlb
|
||||
|
||||
from tlb
|
||||
into binRepoFile.parentFile
|
||||
|
||||
doFirst {
|
||||
assert file(BIN_REPO).exists() : "Bin repo doesn't exist"
|
||||
file(binRepoFile).parentFile.mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,13 +3,6 @@
|
|||
##MODULE IP: MIT
|
||||
Module.manifest||GHIDRA||||END|
|
||||
README.md||GHIDRA||||END|
|
||||
data/debugger-launchers/kernel-dbgeng.bat||GHIDRA||||END|
|
||||
data/debugger-launchers/local-dbgeng-attach.bat||GHIDRA||||END|
|
||||
data/debugger-launchers/local-dbgeng-ext.bat||GHIDRA||||END|
|
||||
data/debugger-launchers/local-dbgeng-trace.bat||GHIDRA||||END|
|
||||
data/debugger-launchers/local-dbgeng.bat||GHIDRA||||END|
|
||||
data/debugger-launchers/remote-dbgeng.bat||GHIDRA||||END|
|
||||
data/debugger-launchers/svrcx-dbgeng.bat||GHIDRA||||END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
|
||||
src/main/help/help/topics/dbgeng/dbgeng.html||GHIDRA||||END|
|
||||
src/main/py/LICENSE||GHIDRA||||END|
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
:: ###
|
||||
:: IP: GHIDRA
|
||||
::
|
||||
:: Licensed under the Apache License, Version 2.0 (the "License");
|
||||
:: you may not use this file except in compliance with the License.
|
||||
:: You may obtain a copy of the License at
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing, software
|
||||
:: distributed under the License is distributed on an "AS IS" BASIS,
|
||||
:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
:: See the License for the specific language governing permissions and
|
||||
:: limitations under the License.
|
||||
:: ##
|
||||
::@title dbgeng kernel
|
||||
::@desc <html><body width="300px">
|
||||
::@desc <h3>Kernel debugging using <tt>dbgeng</tt> (in a Python interpreter)</h3>
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
:: ###
|
||||
:: IP: GHIDRA
|
||||
::
|
||||
:: Licensed under the Apache License, Version 2.0 (the "License");
|
||||
:: you may not use this file except in compliance with the License.
|
||||
:: You may obtain a copy of the License at
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing, software
|
||||
:: distributed under the License is distributed on an "AS IS" BASIS,
|
||||
:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
:: See the License for the specific language governing permissions and
|
||||
:: limitations under the License.
|
||||
:: ##
|
||||
::@title dbgeng attach
|
||||
::@desc <html><body width="300px">
|
||||
::@desc <h3>Attach with <tt>dbgeng</tt> (in a Python interpreter)</h3>
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
:: ###
|
||||
:: IP: GHIDRA
|
||||
::
|
||||
:: Licensed under the Apache License, Version 2.0 (the "License");
|
||||
:: you may not use this file except in compliance with the License.
|
||||
:: You may obtain a copy of the License at
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing, software
|
||||
:: distributed under the License is distributed on an "AS IS" BASIS,
|
||||
:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
:: See the License for the specific language governing permissions and
|
||||
:: limitations under the License.
|
||||
:: ##
|
||||
::@title dbgeng extra options
|
||||
::@image-opt env:OPT_TARGET_IMG
|
||||
::@desc <html><body width="300px">
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
:: ###
|
||||
:: IP: GHIDRA
|
||||
::
|
||||
:: Licensed under the Apache License, Version 2.0 (the "License");
|
||||
:: you may not use this file except in compliance with the License.
|
||||
:: You may obtain a copy of the License at
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing, software
|
||||
:: distributed under the License is distributed on an "AS IS" BASIS,
|
||||
:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
:: See the License for the specific language governing permissions and
|
||||
:: limitations under the License.
|
||||
:: ##
|
||||
::@title dbgeng TTD
|
||||
::@desc <html><body width="300px">
|
||||
::@desc <h3>Open trace with <tt>dbgeng</tt> (in a Python interpreter)</h3>
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
:: ###
|
||||
:: IP: GHIDRA
|
||||
::
|
||||
:: Licensed under the Apache License, Version 2.0 (the "License");
|
||||
:: you may not use this file except in compliance with the License.
|
||||
:: You may obtain a copy of the License at
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing, software
|
||||
:: distributed under the License is distributed on an "AS IS" BASIS,
|
||||
:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
:: See the License for the specific language governing permissions and
|
||||
:: limitations under the License.
|
||||
:: ##
|
||||
::@title dbgeng
|
||||
::@image-opt env:OPT_TARGET_IMG
|
||||
::@desc <html><body width="300px">
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
:: ###
|
||||
:: IP: GHIDRA
|
||||
::
|
||||
:: Licensed under the Apache License, Version 2.0 (the "License");
|
||||
:: you may not use this file except in compliance with the License.
|
||||
:: You may obtain a copy of the License at
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing, software
|
||||
:: distributed under the License is distributed on an "AS IS" BASIS,
|
||||
:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
:: See the License for the specific language governing permissions and
|
||||
:: limitations under the License.
|
||||
:: ##
|
||||
::@title dbgeng remote
|
||||
::@desc <html><body width="300px">
|
||||
::@desc <h3>Connect to a remote debugger (via the .server interface)</h3>
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
:: ###
|
||||
:: IP: GHIDRA
|
||||
::
|
||||
:: Licensed under the Apache License, Version 2.0 (the "License");
|
||||
:: you may not use this file except in compliance with the License.
|
||||
:: You may obtain a copy of the License at
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing, software
|
||||
:: distributed under the License is distributed on an "AS IS" BASIS,
|
||||
:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
:: See the License for the specific language governing permissions and
|
||||
:: limitations under the License.
|
||||
:: ##
|
||||
::@title dbgeng svrcx
|
||||
::@image-opt env:OPT_TARGET_IMG
|
||||
::@desc <html><body width="300px">
|
||||
|
|
|
@ -2,16 +2,10 @@
|
|||
##MODULE IP: JSch License
|
||||
Module.manifest||GHIDRA||||END|
|
||||
README.md||GHIDRA||||END|
|
||||
data/debugger-launchers/local-gdb.ps1||GHIDRA||||END|
|
||||
data/debugger-launchers/qemu-sys-gdb.ps1||GHIDRA||||END|
|
||||
data/debugger-launchers/remote-gdb.ps1||GHIDRA||||END|
|
||||
data/debugger-launchers/ssh-gdb.ps1||GHIDRA||||END|
|
||||
data/debugger-launchers/ssh-gdbserver.ps1||GHIDRA||||END|
|
||||
data/scripts/fallback_info_proc_mappings.gdb||GHIDRA||||END|
|
||||
data/scripts/fallback_maintenance_info_sections.gdb||GHIDRA||||END|
|
||||
data/scripts/getpid-linux-i386.gdb||GHIDRA||||END|
|
||||
data/scripts/wine32_info_proc_mappings.gdb||GHIDRA||||END|
|
||||
data/support/gdbsetuputils.ps1||GHIDRA||||END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
|
||||
src/main/help/help/topics/gdb/gdb.html||GHIDRA||||END|
|
||||
src/main/help/help/topics/gdb/images/GdbLauncher.png||GHIDRA||||END|
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
#@title gdb
|
||||
#@image-opt arg:1
|
||||
#@desc <html><body width="300px">
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
#@title gdb + qemu-system
|
||||
#@image-opt env:OPT_TARGET_IMG
|
||||
#@desc <html><body width="300px">
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
#@title gdb remote
|
||||
#@image-opt arg:1
|
||||
#@desc <html><body width="300px">
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
#@title gdb via ssh
|
||||
#@image-opt arg:1
|
||||
#@desc <html><body width="300px">
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
#@title gdb + gdbserver via ssh
|
||||
#@image-opt arg:1
|
||||
#@desc <html><body width="300px">
|
||||
|
|
|
@ -1,4 +1,18 @@
|
|||
|
||||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
. $Env:MODULE_Debugger_rmi_trace_HOME\data\support\setuputils.ps1
|
||||
|
||||
function Add-Gdb-Init-Args {
|
||||
|
|
|
@ -4,12 +4,6 @@
|
|||
Module.manifest||GHIDRA||||END|
|
||||
README.md||GHIDRA||||END|
|
||||
build.gradle||GHIDRA||||END|
|
||||
data/debugger-launchers/android-lldb.ps1||GHIDRA||||END|
|
||||
data/debugger-launchers/kernel-lldb.ps1||GHIDRA||||END|
|
||||
data/debugger-launchers/local-lldb.ps1||GHIDRA||||END|
|
||||
data/debugger-launchers/remote-lldb.ps1||GHIDRA||||END|
|
||||
data/debugger-launchers/ssh-lldb.ps1||GHIDRA||||END|
|
||||
data/support/lldbsetuputils.ps1||GHIDRA||||END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
|
||||
src/main/help/help/topics/lldb/lldb.html||GHIDRA||||END|
|
||||
src/main/py/LICENSE||GHIDRA||||END|
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
#@title lldb Android
|
||||
#@image-opt arg:1
|
||||
#@desc <html><body width="300px">
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
#@title lldb kernel (kdp)
|
||||
#@desc <html><body width="300px">
|
||||
#@desc <h3>Launch with local <tt>lldb</tt> and connect to a remote kernel</h3>
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
#@title lldb
|
||||
#@image-opt arg:1
|
||||
#@desc <html><body width="300px">
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
#@title lldb remote (gdb)
|
||||
#@image-opt arg:1
|
||||
#@desc <html><body width="300px">
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
#@title lldb via ssh
|
||||
#@image-opt arg:1
|
||||
#@desc <html><body width="300px">
|
||||
|
|
|
@ -1,4 +1,18 @@
|
|||
|
||||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
. $Env:MODULE_Debugger_rmi_trace_HOME\data\support\setuputils.ps1
|
||||
|
||||
function Add-Lldb-Init-Args {
|
||||
|
|
|
@ -5,7 +5,6 @@ DEVNOTES.txt||GHIDRA||||END|
|
|||
Module.manifest||GHIDRA||||END|
|
||||
README.md||GHIDRA||||END|
|
||||
data/ExtensionPoint.manifest||GHIDRA||||END|
|
||||
data/support/setuputils.ps1||GHIDRA||||END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
|
||||
src/main/help/help/topics/TraceRmiConnectionManagerPlugin/TraceRmiConnectionManagerPlugin.html||GHIDRA||||END|
|
||||
src/main/help/help/topics/TraceRmiConnectionManagerPlugin/images/ConnectDialog.png||GHIDRA||||END|
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
function Find-App-Properties {
|
||||
[IO.FileInfo] $props = "$Env:GHIDRA_HOME\application.properties"
|
||||
if ($props.Exists) {
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -30,8 +30,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
|
||||
import docking.ActionContext;
|
||||
import docking.ComponentProvider;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import docking.action.ToolBarData;
|
||||
import docking.action.*;
|
||||
import docking.menu.ActionState;
|
||||
import docking.menu.MultiStateDockingAction;
|
||||
import docking.widgets.*;
|
||||
|
@ -345,7 +344,8 @@ public class SampleGraphProvider extends ComponentProviderAdapter {
|
|||
private void addLayoutAction() {
|
||||
|
||||
MultiStateDockingAction<LayoutProvider<SampleVertex, SampleEdge, SampleGraph>> layoutAction =
|
||||
new MultiStateDockingAction<>(RELAYOUT_GRAPH_ACTION_NAME, plugin.getName()) {
|
||||
new MultiStateDockingAction<>(RELAYOUT_GRAPH_ACTION_NAME, plugin.getName(),
|
||||
KeyBindingType.SHARED) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -25,10 +25,10 @@ import ghidra.util.task.*;
|
|||
/**
|
||||
* Provides the ability to synchronize concurrent connection task
|
||||
* instances within the same thread. This can occur within the swing thread due to the presence
|
||||
* of a modal task dialog event queue. It also allows password cancelation to be propogated to the
|
||||
* of a modal task dialog event queue. It also allows password cancellation to be propagated to the
|
||||
* other tasks(s).
|
||||
*/
|
||||
public class BSimDBConnectTaskCoordinator {
|
||||
public class BSimDBConnectTaskManager {
|
||||
|
||||
private final BSimServerInfo serverInfo;
|
||||
|
||||
|
@ -36,7 +36,7 @@ public class BSimDBConnectTaskCoordinator {
|
|||
private boolean isCancelled = false;
|
||||
private int count = 0;
|
||||
|
||||
public BSimDBConnectTaskCoordinator(BSimServerInfo serverInfo) {
|
||||
public BSimDBConnectTaskManager(BSimServerInfo serverInfo) {
|
||||
this.serverInfo = serverInfo;
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ public class BSimDBConnectTaskCoordinator {
|
|||
* Initiate a DB connection.
|
||||
* @param connectionSupplier DB connection supplier
|
||||
* @return DB connection
|
||||
* @throws SQLException if a database connection error occured
|
||||
* @throws SQLException if a database connection error occurred
|
||||
* @throws CancelledSQLException if task was cancelled (password entry cancelled)
|
||||
*/
|
||||
public Connection getConnection(DBConnectionSupplier connectionSupplier) throws SQLException {
|
||||
|
@ -66,7 +66,7 @@ public class BSimDBConnectTaskCoordinator {
|
|||
.launchModal();
|
||||
//@formatter:on
|
||||
|
||||
synchronized (BSimDBConnectTaskCoordinator.this) {
|
||||
synchronized (BSimDBConnectTaskManager.this) {
|
||||
Connection c = connectTask.getConnection();
|
||||
if (c != null) {
|
||||
return c;
|
||||
|
@ -85,7 +85,7 @@ public class BSimDBConnectTaskCoordinator {
|
|||
}
|
||||
}
|
||||
finally {
|
||||
synchronized (BSimDBConnectTaskCoordinator.this) {
|
||||
synchronized (BSimDBConnectTaskManager.this) {
|
||||
if (--count == 0) {
|
||||
clear();
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ public class BSimDBConnectTaskCoordinator {
|
|||
*/
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
synchronized (BSimDBConnectTaskCoordinator.this) {
|
||||
synchronized (BSimDBConnectTaskManager.this) {
|
||||
monitor.setMessage("Connecting...");
|
||||
++count;
|
||||
if (isCancelled) {
|
|
@ -109,11 +109,11 @@ public class BSimPostgresDBConnectionManager {
|
|||
private boolean successfulConnection = false;
|
||||
|
||||
private BasicDataSource bds = new BasicDataSource();
|
||||
private BSimDBConnectTaskCoordinator taskCoordinator;
|
||||
private BSimDBConnectTaskManager taskManager;
|
||||
|
||||
private BSimPostgresDataSource(BSimServerInfo serverInfo) {
|
||||
this.serverInfo = serverInfo;
|
||||
this.taskCoordinator = new BSimDBConnectTaskCoordinator(serverInfo);
|
||||
this.taskManager = new BSimDBConnectTaskManager(serverInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -254,7 +254,7 @@ public class BSimPostgresDBConnectionManager {
|
|||
|
||||
setDefaultProperties();
|
||||
|
||||
return taskCoordinator.getConnection(() -> connect());
|
||||
return taskManager.getConnection(() -> connect());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -306,16 +306,18 @@ public class BSimPostgresDBConnectionManager {
|
|||
loginError = "Access denied: " + serverInfo;
|
||||
Msg.error(this, loginError);
|
||||
}
|
||||
|
||||
// Use Ghidra's authentication infrastructure
|
||||
connectionType = ConnectionType.SSL_Password_Authentication; // Try again with a password
|
||||
// fallthru to second attempt at getConnection
|
||||
// Try again with a password; fallthrough to second attempt at getConnection
|
||||
connectionType = ConnectionType.SSL_Password_Authentication;
|
||||
}
|
||||
else if (e.getMessage().contains("SSL on") &&
|
||||
e.getMessage().contains("no pg_hba.conf entry")) {
|
||||
connectionType = ConnectionType.Unencrypted_No_Authentication; // Try again without any SSL
|
||||
|
||||
// Try again without any SSL; fallthrough to second attempt at getConnection
|
||||
connectionType = ConnectionType.Unencrypted_No_Authentication;
|
||||
bds.removeConnectionProperty("sslmode");
|
||||
bds.removeConnectionProperty("sslfactory");
|
||||
// fallthru to second attempt at getConnection
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
|
@ -364,6 +366,7 @@ public class BSimPostgresDBConnectionManager {
|
|||
catch (SQLException e) {
|
||||
if ((clientAuthenticator instanceof DefaultClientAuthenticator) &&
|
||||
e.getMessage().contains("password authentication failed")) {
|
||||
|
||||
// wrong password provided via popup dialog - try again
|
||||
loginError = "Access denied: " + serverInfo;
|
||||
continue;
|
||||
|
|
|
@ -43,8 +43,8 @@ public class BSimH2FileDBConnectionManager {
|
|||
private static boolean shutdownHookInstalled = false;
|
||||
|
||||
/**
|
||||
* Get all H2 File DB data sorces which exist in the JVM.
|
||||
* @return all H2 File DB data sorces
|
||||
* Get all H2 File DB data sources which exist in the JVM.
|
||||
* @return all H2 File DB data sources
|
||||
*/
|
||||
public static synchronized Collection<BSimH2FileDataSource> getAllDataSources() {
|
||||
// Create copy to avoid potential concurrent modification
|
||||
|
@ -138,11 +138,11 @@ public class BSimH2FileDBConnectionManager {
|
|||
private boolean successfulConnection = false;
|
||||
|
||||
private BasicDataSource bds = new BasicDataSource();
|
||||
private BSimDBConnectTaskCoordinator taskCoordinator;
|
||||
private BSimDBConnectTaskManager taskManager;
|
||||
|
||||
private BSimH2FileDataSource(BSimServerInfo serverInfo) {
|
||||
this.serverInfo = serverInfo;
|
||||
this.taskCoordinator = new BSimDBConnectTaskCoordinator(serverInfo);
|
||||
this.taskManager = new BSimDBConnectTaskManager(serverInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -159,7 +159,7 @@ public class BSimH2FileDBConnectionManager {
|
|||
* Delete the database files associated with this H2 File DB. This will fail immediately
|
||||
* if active connections exist. Otherwise removal will be attempted and this data source
|
||||
* will no longer be valid.
|
||||
* @return true if DB sucessfully removed
|
||||
* @return true if DB successfully removed
|
||||
*/
|
||||
public synchronized boolean delete() {
|
||||
|
||||
|
@ -278,7 +278,7 @@ public class BSimH2FileDBConnectionManager {
|
|||
* Get a connection to the H2 file database.
|
||||
* It is important to note that if the database does not exist and empty one will
|
||||
* be created. The {@link #exists()} method should be used to check for the database
|
||||
* existance prior to connecting the first time.
|
||||
* existence prior to connecting the first time.
|
||||
* @return database connection
|
||||
* @throws SQLException if a database error occurs
|
||||
*/
|
||||
|
@ -294,7 +294,7 @@ public class BSimH2FileDBConnectionManager {
|
|||
|
||||
setDefaultProperties();
|
||||
|
||||
return taskCoordinator.getConnection(() -> connect());
|
||||
return taskManager.getConnection(() -> connect());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -317,6 +317,7 @@ src/main/help/help/topics/DataTypeEditors/images/BytesNumberInputDialog.png||GHI
|
|||
src/main/help/help/topics/DataTypeEditors/images/Dialog.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DataTypeEditors/images/Dialog_Create_Pointer.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DataTypeEditors/images/Dialog_Multiple_Match.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DataTypeEditors/images/Dialog_SearchMode.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DataTypeEditors/images/Dialog_Select_Tree.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DataTypeEditors/images/Dialog_Single_Match.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png||GHIDRA||||END|
|
||||
|
|
|
@ -14,7 +14,7 @@ MachoRelocationHandler
|
|||
ElfRelocationHandler
|
||||
ElfExtension
|
||||
RelocationFixupHandler
|
||||
CodeComparisonPanel
|
||||
CodeComparisonView
|
||||
InstructionSkipper
|
||||
DataTypeReferenceFinder
|
||||
ChecksumAlgorithm
|
||||
|
|
|
@ -19,6 +19,59 @@
|
|||
<P align="center"><IMG border="0" src="images/Dialog.png" alt=""><BR>
|
||||
<I>Data Type Chooser Dialog</I></P>
|
||||
|
||||
|
||||
<P>As you type text in the field, any potential matches will be displayed in the completion
|
||||
window, which is described below.
|
||||
</P>
|
||||
|
||||
<A NAME="SearchMode" />
|
||||
<P>
|
||||
The way matches are determined depends upon the search
|
||||
mode you are in. The current mode is displayed at the right side of the text field,
|
||||
indicated with a single character. Hovering over the character will show a tool tip
|
||||
window that shows the name for the current mode.
|
||||
</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG border="0" src="help/shared/tip.png" alt="">To change the search mode, click on
|
||||
the seach mode character at the right side of the text field.
|
||||
</P>
|
||||
|
||||
<P>
|
||||
You can also change the search mode using <B>Ctrl Down</B> and <B>Ctrl Up</B> to
|
||||
change the mode forward and backward, respectively.
|
||||
</P>
|
||||
|
||||
<P align="center"><IMG border="0" src="images/Dialog_SearchMode.png" alt=""><BR>
|
||||
<I>Data Type Chooser Dialog</I></P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P>
|
||||
By default, this chooser uses a <B>Starts With</B> matching mode. Any text typed will be
|
||||
used to match all data type with a name that begins with the current search text.
|
||||
</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG border="0" src="help/shared/tip.png" alt="">This data type selection chooser
|
||||
performs the best with the 'starts with' setting. For a large number of data types,
|
||||
this is the recommended search setting.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG border="0" src="help/shared/note.yellow.png" alt="">The text used to match is
|
||||
based on the cursor position in the field. All text from the beginning up to the
|
||||
cursor position will be used for the match. This allows you to arrow left and right
|
||||
to control the matching list.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="completion"></A> Completion Window</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 5.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
|
@ -494,8 +494,10 @@
|
|||
</P>
|
||||
|
||||
|
||||
|
||||
<H3><A name="Decompiler_Code_Comparison_Actions"></A>Decompiler Code Comparison Actions</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
||||
<H4><A name="Compare_Matching_Callees"></A>Compare Matching Callees</H4>
|
||||
<BLOCKQUOTE>
|
||||
<P> This action is available on matched tokens corresponding to function calls. It will open
|
||||
|
@ -512,8 +514,31 @@
|
|||
<P> This toggles whether or not constants must be exactly the same value to be a match in
|
||||
the Decompiler Diff View. </P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
|
||||
<H2><A name="FunctionGraph_Diff_View"></A>Function Graph Diff View</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The <B><I> Function Graph Diff View</I></B> shows a pair of Function Graphs side by
|
||||
side.
|
||||
</P>
|
||||
<BR><BR>
|
||||
|
||||
<H3><A name="FunctionGraph_Code_Comparison_Actions"></A>Function Graph Code Comparison Actions</H3>
|
||||
|
||||
<H4><A name="Dual_Function_Graph_View_Toggle_Orientation"></A>Show Function Graphs Side-by-Side</H4>
|
||||
<BLOCKQUOTE>
|
||||
<P> This toggles the Function Graph panels between a vertical split and a horizontal split. </P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
|
||||
<H2><A name="Compare Multiple Functions"></A>Comparing Multiple Functions</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
|
|
@ -25,7 +25,7 @@ import docking.widgets.EmptyBorderButton;
|
|||
import docking.widgets.TitledPanel;
|
||||
import docking.widgets.button.GRadioButton;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelScrollCoordinator;
|
||||
import docking.widgets.label.GIconLabel;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.GhidraOptions;
|
||||
|
@ -214,7 +214,7 @@ class ExternalAddConflictPanel extends JPanel implements CodeFormatService {
|
|||
latestPanel.setProgram(latestProgram);
|
||||
myPanel.setProgram(myProgram);
|
||||
|
||||
new FieldPanelCoordinator(
|
||||
new FieldPanelScrollCoordinator(
|
||||
new FieldPanel[] { latestPanel.getFieldPanel(), myPanel.getFieldPanel() });
|
||||
|
||||
buttonGroup = new ButtonGroup();
|
||||
|
|
|
@ -27,7 +27,7 @@ import docking.widgets.EmptyBorderButton;
|
|||
import docking.widgets.TitledPanel;
|
||||
import docking.widgets.checkbox.GCheckBox;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelScrollCoordinator;
|
||||
import docking.widgets.fieldpanel.support.BackgroundColorModel;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.GhidraOptions;
|
||||
|
@ -66,7 +66,7 @@ public class ListingMergePanel extends JPanel
|
|||
private JComponent bottomComp;
|
||||
protected TitledPanel[] titlePanels;
|
||||
private ListingPanel[] listingPanels;
|
||||
private FieldPanelCoordinator coordinator;
|
||||
private FieldPanelScrollCoordinator coordinator;
|
||||
private FormatManager formatMgr;
|
||||
private MultiListingLayoutModel multiModel;
|
||||
private Program[] programs = new Program[4];
|
||||
|
@ -120,7 +120,7 @@ public class ListingMergePanel extends JPanel
|
|||
}
|
||||
|
||||
backgroundColorModel.addChangeListener(backgroundChangeListener);
|
||||
coordinator = new FieldPanelCoordinator(fieldPanels);
|
||||
coordinator = new FieldPanelScrollCoordinator(fieldPanels);
|
||||
|
||||
titlePanels[RESULT].addTitleComponent(new ShowHeaderButton());
|
||||
|
||||
|
|
|
@ -130,9 +130,9 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||
private String stringContent;
|
||||
private boolean includeQuotesForStringData;
|
||||
|
||||
public CodeBrowserClipboardProvider(PluginTool tool, ComponentProvider codeViewerProvider) {
|
||||
public CodeBrowserClipboardProvider(PluginTool tool, ComponentProvider componentProvider) {
|
||||
this.tool = tool;
|
||||
this.componentProvider = codeViewerProvider;
|
||||
this.componentProvider = componentProvider;
|
||||
|
||||
PAINT_CONTEXT.setTextCopying(true);
|
||||
|
||||
|
@ -140,7 +140,6 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
|||
includeQuotesForStringData =
|
||||
!options.getBoolean(ClipboardPlugin.REMOVE_QUOTES_OPTION, false);
|
||||
options.addOptionsChangeListener(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -36,7 +36,7 @@ import docking.dnd.*;
|
|||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.HoverHandler;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelScrollCoordinator;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import docking.widgets.tab.GTabPanel;
|
||||
import generic.theme.GIcon;
|
||||
|
@ -105,7 +105,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
private ListingPanel otherPanel;
|
||||
private CoordinatedListingPanelListener coordinatedListingPanelListener;
|
||||
private FormatManager formatMgr;
|
||||
private FieldPanelCoordinator coordinator;
|
||||
private FieldPanelScrollCoordinator coordinator;
|
||||
private ProgramSelectionListener liveProgramSelectionListener = (selection, trigger) -> {
|
||||
liveSelection = selection;
|
||||
updateSubTitle();
|
||||
|
@ -753,7 +753,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
|||
ListingModel otherAlignedModel = multiModel.getAlignedModel(1);
|
||||
listingPanel.setListingModel(myAlignedModel);
|
||||
lp.setListingModel(otherAlignedModel);
|
||||
coordinator = new FieldPanelCoordinator(
|
||||
coordinator = new FieldPanelScrollCoordinator(
|
||||
new FieldPanel[] { listingPanel.getFieldPanel(), lp.getFieldPanel() });
|
||||
addHoverServices(otherPanel);
|
||||
HoverHandler hoverHandler = listingPanel.getFieldPanel().getHoverHandler();
|
||||
|
|
|
@ -18,8 +18,11 @@ package ghidra.app.plugin.core.function.editor;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.help.UnsupportedOperationException;
|
||||
import javax.swing.ListCellRenderer;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.widgets.DropDownSelectionTextField;
|
||||
import docking.widgets.DropDownTextFieldDataModel;
|
||||
import docking.widgets.list.GListCellRenderer;
|
||||
|
@ -37,6 +40,11 @@ public class RegisterDropDownSelectionDataModel implements DropDownTextFieldData
|
|||
this.registers = registers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SearchMode> getSupportedSearchModes() {
|
||||
return List.of(SearchMode.STARTS_WITH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListCellRenderer<Register> getListRenderer() {
|
||||
return new GListCellRenderer<Register>();
|
||||
|
@ -54,11 +62,20 @@ public class RegisterDropDownSelectionDataModel implements DropDownTextFieldData
|
|||
|
||||
@Override
|
||||
public List<Register> getMatchingData(String searchText) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Method no longer supported. Instead, call getMatchingData(String, SearchMode)");
|
||||
}
|
||||
|
||||
if (searchText == null || searchText.length() == 0) {
|
||||
@Override
|
||||
public List<Register> getMatchingData(String searchText, SearchMode searchMode) {
|
||||
if (StringUtils.isBlank(searchText)) {
|
||||
return registers;
|
||||
}
|
||||
|
||||
if (searchMode != SearchMode.STARTS_WITH) {
|
||||
throw new IllegalArgumentException("Unsupported SearchMode: " + searchMode);
|
||||
}
|
||||
|
||||
searchText = searchText.toLowerCase();
|
||||
|
||||
List<Register> regList = new ArrayList<>();
|
||||
|
@ -85,5 +102,4 @@ public class RegisterDropDownSelectionDataModel implements DropDownTextFieldData
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -155,11 +155,18 @@ public class ExternalReferencesProvider extends ComponentProviderAdapter {
|
|||
JPanel panel = new JPanel(new BorderLayout());
|
||||
tableModel = new ExternalNamesTableModel();
|
||||
table = new GhidraTable(tableModel);
|
||||
table.getSelectionModel().addListSelectionListener(e -> {
|
||||
if (e.getValueIsAdjusting()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JScrollPane sp = new JScrollPane(table);
|
||||
contextChanged();
|
||||
});
|
||||
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
|
||||
ToolTipManager.sharedInstance().registerComponent(table);
|
||||
|
||||
JScrollPane sp = new JScrollPane(table);
|
||||
panel.add(sp, BorderLayout.CENTER);
|
||||
|
||||
String namePrefix = "External Programs";
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -16,8 +16,6 @@
|
|||
package ghidra.app.plugin.core.script;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
|
@ -28,7 +26,6 @@ import docking.widgets.*;
|
|||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.script.ScriptInfo;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.UserSearchUtils;
|
||||
|
||||
/**
|
||||
* A widget that allows the user to choose an existing script by typing its name or picking it
|
||||
|
@ -222,24 +219,10 @@ public class ScriptSelectionEditor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<ScriptInfo> getMatchingData(String searchText) {
|
||||
|
||||
// This pattern will: 1) allow users to match the typed text anywhere in the
|
||||
// script names and 2) allow the use of globbing characters
|
||||
Pattern pattern = UserSearchUtils.createContainsPattern(searchText, true,
|
||||
Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
|
||||
|
||||
List<ScriptInfo> results = new ArrayList<>();
|
||||
for (ScriptInfo info : data) {
|
||||
String name = info.getName();
|
||||
Matcher m = pattern.matcher(name);
|
||||
if (m.matches()) {
|
||||
results.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
public List<SearchMode> getSupportedSearchModes() {
|
||||
return List.of(SearchMode.CONTAINS, SearchMode.WILDCARD, SearchMode.STARTS_WITH);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ScriptSelectionTextField extends DropDownSelectionTextField<ScriptInfo> {
|
||||
|
|
|
@ -19,6 +19,7 @@ import static ghidra.framework.model.DomainObjectEvent.*;
|
|||
import static ghidra.program.util.ProgramEvent.*;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Point;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.ClipboardOwner;
|
||||
import java.awt.event.MouseAdapter;
|
||||
|
@ -167,7 +168,8 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
return;
|
||||
}
|
||||
|
||||
maybeGoToSymbol();
|
||||
SymbolNode symbolNode = getSelectedSymbolNode();
|
||||
maybeGoToSymbol(symbolNode);
|
||||
contextChanged();
|
||||
});
|
||||
|
||||
|
@ -175,14 +177,24 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
|
||||
// This code serves to perform navigation in the case that the selection handler
|
||||
// above does not, as is the case when the node is already selected. This code
|
||||
// will get called on the mouse release, whereas the selection handler gets called
|
||||
// on the mouse pressed.
|
||||
// This code serves to perform navigation in the case that the selection handler
|
||||
// above does not, as is the case when the clicked node is already selected. This
|
||||
// code will get called on the mouse clicked, whereas the selection handler gets
|
||||
// called on the mouse pressed.
|
||||
// For now, just attempt to perform the goto. It may get called twice, but this
|
||||
// should have no real impact on performance.
|
||||
Point p = e.getPoint();
|
||||
GTreeNode clickedNode = newTree.getNodeForLocation(p.x, p.y);
|
||||
if (clickedNode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
maybeGoToSymbol();
|
||||
SymbolNode symbolNode = getSelectedSymbolNode();
|
||||
if (!clickedNode.equals(symbolNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
maybeGoToSymbol(symbolNode);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -211,19 +223,24 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
private void maybeGoToSymbol() {
|
||||
|
||||
private SymbolNode getSelectedSymbolNode() {
|
||||
TreePath[] paths = tree.getSelectionPaths();
|
||||
if (paths == null || paths.length != 1) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
Object object = paths[0].getLastPathComponent();
|
||||
if (!(object instanceof SymbolNode)) {
|
||||
return;
|
||||
if (object instanceof SymbolNode symbolNode) {
|
||||
return symbolNode;
|
||||
}
|
||||
|
||||
SymbolNode node = (SymbolNode) object;
|
||||
return null;
|
||||
}
|
||||
|
||||
private void maybeGoToSymbol(SymbolNode node) {
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
Symbol symbol = node.getSymbol();
|
||||
SymbolType type = symbol.getSymbolType();
|
||||
if (!type.isNamespace() || type == SymbolType.FUNCTION) {
|
||||
|
@ -613,6 +630,11 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
.each(SYMBOL_REMOVED).call(this::processSymbolRemoved)
|
||||
.each(EXTERNAL_ENTRY_ADDED, EXTERNAL_ENTRY_REMOVED)
|
||||
.call(this::processExternalEntryChanged)
|
||||
.any(EXTERNAL_PATH_CHANGED, EXTERNAL_NAME_ADDED,
|
||||
EXTERNAL_NAME_REMOVED, EXTERNAL_NAME_CHANGED)
|
||||
// Rather than try to find each affected symbol, it is easier to just reload
|
||||
// the tree. This is infrequent enough that it should not be disruptive.
|
||||
.call(this::reloadTree)
|
||||
|
||||
// handle function changes specially so that we can perform coalesce changes
|
||||
.any(FUNCTION_CHANGED).call(this::processAllFunction)
|
||||
|
|
|
@ -68,13 +68,18 @@ public abstract class SymbolCategoryNode extends SymbolTreeNode {
|
|||
@Override
|
||||
public List<GTreeNode> generateChildren(TaskMonitor monitor) throws CancelledException {
|
||||
if (!isEnabled) {
|
||||
return Collections.emptyList();
|
||||
return List.of();
|
||||
}
|
||||
|
||||
SymbolTreeRootNode root = (SymbolTreeRootNode) getRoot();
|
||||
if (root == null) {
|
||||
// this can happen if the tree is reloaded while we are searching in a background task
|
||||
return List.of();
|
||||
}
|
||||
|
||||
SymbolType symbolType = symbolCategory.getSymbolType();
|
||||
List<GTreeNode> list = getSymbols(symbolType, monitor);
|
||||
monitor.checkCancelled();
|
||||
SymbolTreeRootNode root = (SymbolTreeRootNode) getRoot();
|
||||
int groupThreshold = root.getNodeGroupThreshold();
|
||||
return OrganizationNode.organize(list, groupThreshold, monitor);
|
||||
}
|
||||
|
@ -322,6 +327,9 @@ public abstract class SymbolCategoryNode extends SymbolTreeNode {
|
|||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -482,7 +482,7 @@ public abstract class AbstractSymbolTableModel extends AddressBasedTableModel<Sy
|
|||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
Symbol symbol = rowObject.getSymbol();
|
||||
if (symbol == null || symbol.isDeleted()) {
|
||||
if (symbol == null || symbol.isDeleted() || symbol.isDynamic()) {
|
||||
return null;
|
||||
}
|
||||
return symbol.getParentNamespace().getName(true);
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -19,6 +19,7 @@ import java.util.Collection;
|
|||
|
||||
import ghidra.features.base.codecompare.model.FunctionComparisonModel;
|
||||
import ghidra.features.base.codecompare.model.MatchedFunctionComparisonModel;
|
||||
import ghidra.features.base.codecompare.panel.FunctionComparisonPanel;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import utility.function.Callback;
|
||||
|
||||
|
@ -84,6 +85,17 @@ public interface FunctionComparisonService {
|
|||
* @param closeListener an optional callback if the client wants to be notified when the
|
||||
* associated function comparison windows is closed.
|
||||
*/
|
||||
public void createCustomComparison(FunctionComparisonModel model,
|
||||
Callback closeListener);
|
||||
public void createCustomComparison(FunctionComparisonModel model, Callback closeListener);
|
||||
|
||||
/**
|
||||
* Creates a new comparison view that the caller can install into their UI. This is in contrast
|
||||
* with {@link #createCustomComparison(FunctionComparisonModel, Callback)}, which will install
|
||||
* the new comparison into an existing UI.
|
||||
* <p>
|
||||
* Note: clients are responsible for calling {@link FunctionComparisonPanel#dispose()} when done
|
||||
* using the panel.
|
||||
*
|
||||
* @return the new panel
|
||||
*/
|
||||
public FunctionComparisonPanel createComparisonViewer();
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@ public class ImageRuntimeFunctionEntries_X86 implements ImageRuntimeFunctionEntr
|
|||
*/
|
||||
public void markup(Program program) throws DuplicateNameException, IOException {
|
||||
|
||||
if (!unwindInfo.hasChainedUnwindInfo()) {
|
||||
if (beginAddress != 0 && !unwindInfo.hasChainedUnwindInfo()) {
|
||||
AbstractProgramLoader.markAsFunction(program, null,
|
||||
program.getImageBase().add(beginAddress));
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -161,8 +161,7 @@ public class ResourceDirectoryEntry implements StructConverter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns true if the parent resource directory is named,
|
||||
* false indicates an ID.
|
||||
* {@return true if the parent resource directory is named; false indicates an ID}
|
||||
*/
|
||||
public boolean isNameEntry() {
|
||||
return isNameEntry;
|
||||
|
@ -254,8 +253,14 @@ public class ResourceDirectoryEntry implements StructConverter {
|
|||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType nameStruct = new StructureDataType(NAME + "_" + "NameStruct", 0);
|
||||
nameStruct.add(DWORD, "NameOffset", null);
|
||||
nameStruct.add(DWORD, "NameIsString", null);
|
||||
nameStruct.setPackingEnabled(true);
|
||||
try {
|
||||
nameStruct.addBitField(DWORD, 31, "NameOffset", null);
|
||||
nameStruct.addBitField(DWORD, 1, "NameIsString", null);
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
nameStruct.setCategoryPath(new CategoryPath("/PE"));
|
||||
|
||||
UnionDataType union1 = new UnionDataType(NAME + "_" + "NameUnion");
|
||||
|
@ -265,19 +270,25 @@ public class ResourceDirectoryEntry implements StructConverter {
|
|||
union1.setCategoryPath(new CategoryPath("/PE"));
|
||||
|
||||
StructureDataType offsetStruct = new StructureDataType(NAME + "_" + "DirectoryStruct", 0);
|
||||
offsetStruct.add(DWORD, "OffsetToDirectory", null);
|
||||
offsetStruct.add(DWORD, "DataIsDirectory", null);
|
||||
offsetStruct.setPackingEnabled(true);
|
||||
try {
|
||||
offsetStruct.addBitField(DWORD, 31, "OffsetToDirectory", null);
|
||||
offsetStruct.addBitField(DWORD, 1, "DataIsDirectory", null);
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
UnionDataType union2 = new UnionDataType(NAME + "_" + "DirectoryUnion");
|
||||
union2.add(DWORD, "OffsetToData", null);
|
||||
union2.add(offsetStruct, offsetStruct.getName(), null);
|
||||
|
||||
UnionDataType union3 = new UnionDataType(NAME);
|
||||
union3.add(union1, "NameUnion", null);
|
||||
union3.add(union2, "DirectoryUnion", null);
|
||||
StructureDataType struct = new StructureDataType(NAME, 0);
|
||||
struct.add(union1, "NameUnion", null);
|
||||
struct.add(union2, "DirectoryUnion", null);
|
||||
|
||||
union3.setCategoryPath(new CategoryPath("/PE"));
|
||||
return union3;
|
||||
struct.setCategoryPath(new CategoryPath("/PE"));
|
||||
return struct;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
|
|
|
@ -18,14 +18,18 @@ package ghidra.app.util.datatype;
|
|||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.help.UnsupportedOperationException;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.widgets.DropDownSelectionTextField;
|
||||
import docking.widgets.DropDownTextFieldDataModel;
|
||||
import docking.widgets.button.BrowseButton;
|
||||
|
@ -406,16 +410,37 @@ public class CategoryPathSelectionEditor extends AbstractCellEditor {
|
|||
return categoryPath.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SearchMode> getSupportedSearchModes() {
|
||||
return List.of(SearchMode.CONTAINS, SearchMode.STARTS_WITH, SearchMode.WILDCARD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CategoryPath> getMatchingData(String searchText) {
|
||||
if (searchText == null || searchText.length() == 0) {
|
||||
return Collections.emptyList();
|
||||
throw new UnsupportedOperationException(
|
||||
"Method no longer supported. Instead, call getMatchingData(String, SearchMode)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CategoryPath> getMatchingData(String searchText, SearchMode mode) {
|
||||
if (StringUtils.isBlank(searchText)) {
|
||||
return new ArrayList<>(data);
|
||||
}
|
||||
|
||||
if (!getSupportedSearchModes().contains(mode)) {
|
||||
throw new IllegalArgumentException("Unsupported SearchMode: " + mode);
|
||||
}
|
||||
|
||||
Pattern p = mode.createPattern(searchText);
|
||||
return getMatchingDataRegex(p);
|
||||
}
|
||||
|
||||
private List<CategoryPath> getMatchingDataRegex(Pattern p) {
|
||||
List<CategoryPath> results = new ArrayList<>();
|
||||
for (CategoryPath path : data) {
|
||||
String pathString = path.getPath();
|
||||
if (pathString.contains(searchText)) {
|
||||
Matcher m = p.matcher(pathString);
|
||||
if (m.matches()) {
|
||||
results.add(path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,10 @@ package ghidra.app.util.datatype;
|
|||
|
||||
import java.awt.Component;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.help.UnsupportedOperationException;
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.widgets.DropDownSelectionTextField;
|
||||
|
@ -69,6 +72,11 @@ public class DataTypeDropDownSelectionDataModel implements DropDownTextFieldData
|
|||
return service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SearchMode> getSupportedSearchModes() {
|
||||
return List.of(SearchMode.STARTS_WITH, SearchMode.CONTAINS, SearchMode.WILDCARD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListCellRenderer<DataType> getListRenderer() {
|
||||
return new DataTypeDropDownRenderer();
|
||||
|
@ -86,13 +94,47 @@ public class DataTypeDropDownSelectionDataModel implements DropDownTextFieldData
|
|||
|
||||
@Override
|
||||
public List<DataType> getMatchingData(String searchText) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Method no longer supported. Instead, call getMatchingData(String, SearchMode)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DataType> getMatchingData(String searchText, SearchMode mode) {
|
||||
if (searchText == null || searchText.length() == 0) {
|
||||
// full list results not supported since the data may be too large for user interaction
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<DataType> dataTypeList =
|
||||
if (!getSupportedSearchModes().contains(mode)) {
|
||||
throw new IllegalArgumentException("Unsupported SearchMode: " + mode);
|
||||
}
|
||||
|
||||
if (mode == SearchMode.STARTS_WITH) {
|
||||
return getMatchDataStartsWith(searchText);
|
||||
}
|
||||
|
||||
Pattern p = mode.createPattern(searchText);
|
||||
return getMatchingDataRegex(p);
|
||||
}
|
||||
|
||||
private List<DataType> getMatchDataStartsWith(String searchText) {
|
||||
List<DataType> results =
|
||||
DataTypeUtils.getStartsWithMatchingDataTypes(searchText, dataTypeService);
|
||||
return filterDataTypeList(dataTypeList);
|
||||
return filterDataTypeList(results);
|
||||
}
|
||||
|
||||
private List<DataType> getMatchingDataRegex(Pattern p) {
|
||||
|
||||
List<DataType> results = new ArrayList<>();
|
||||
List<DataType> allTypes = dataTypeService.getSortedDataTypeList();
|
||||
for (DataType dt : allTypes) {
|
||||
String name = dt.getName().toLowerCase();
|
||||
Matcher m = p.matcher(name);
|
||||
if (m.matches()) {
|
||||
results.add(dt);
|
||||
}
|
||||
}
|
||||
return filterDataTypeList(results);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -151,6 +151,8 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
|
|||
editorPanel.add(selectionField);
|
||||
editorPanel.add(browsePanel);
|
||||
|
||||
// This listener is not installed under certain conditions, such as when
|
||||
// setTabCommitsEdit(true) is called.
|
||||
keyListener = new KeyAdapter() {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.nav.NavigationUtils;
|
||||
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
|
@ -54,10 +53,11 @@ public class MnemonicFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
Program program = programManager.getCurrentProgram();
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit codeUnit = listing.getCodeUnitAt(location.getAddress());
|
||||
return checkMemReferences(codeUnit, serviceProvider);
|
||||
return checkMemReferences(codeUnit, sourceNavigatable, serviceProvider);
|
||||
}
|
||||
|
||||
private boolean checkMemReferences(CodeUnit codeUnit, ServiceProvider serviceProvider) {
|
||||
private boolean checkMemReferences(CodeUnit codeUnit, Navigatable navigatable,
|
||||
ServiceProvider serviceProvider) {
|
||||
|
||||
if (codeUnit == null) {
|
||||
return false;
|
||||
|
@ -77,8 +77,8 @@ public class MnemonicFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
|
||||
TableService service = serviceProvider.getService(TableService.class);
|
||||
if (service != null) {
|
||||
Navigatable nav = NavigationUtils.getActiveNavigatable();
|
||||
service.showTable("Mnemonic References", "Mnemonic", model, "References", nav);
|
||||
service.showTable("Mnemonic References", "Mnemonic", model, "References",
|
||||
navigatable);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ public class MnemonicFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
|
||||
GoToService goToService = serviceProvider.getService(GoToService.class);
|
||||
if (goToService != null) {
|
||||
return goToService.goTo(loc);
|
||||
return goToService.goTo(navigatable, loc, navigatable.getProgram());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -83,9 +83,15 @@ public class ProgramLocationTranslator {
|
|||
}
|
||||
}
|
||||
|
||||
// Adjust symbol path for labels if it is part of the location.
|
||||
adjustSymbolPath(saveState, otherSideAddress, address, byteAddress, desiredByteAddress,
|
||||
otherSideLocation.getProgram(), program);
|
||||
String[] symbolPathArray = saveState.getStrings("_SYMBOL_PATH", new String[0]);
|
||||
if (symbolPathArray.length != 0) {
|
||||
// Adjust symbol path for labels if it is part of the location.
|
||||
boolean hasSymbol = adjustSymbolPath(saveState, otherSideAddress, address, byteAddress,
|
||||
desiredByteAddress, otherSideLocation.getProgram(), program);
|
||||
if (!hasSymbol) {
|
||||
return new ProgramLocation(program, desiredByteAddress);
|
||||
}
|
||||
}
|
||||
|
||||
// ref address can't be used with indicated side so remove it.
|
||||
saveState.remove("_REF_ADDRESS");
|
||||
|
@ -186,32 +192,28 @@ public class ProgramLocationTranslator {
|
|||
return ProgramLocation.getLocation(correlator.getProgram(side), saveState);
|
||||
}
|
||||
|
||||
private void adjustSymbolPath(SaveState saveState, Address address, Address desiredAddress,
|
||||
private boolean adjustSymbolPath(SaveState saveState, Address address, Address desiredAddress,
|
||||
Address byteAddress, Address desiredByteAddress, Program program,
|
||||
Program desiredProgram) {
|
||||
|
||||
String[] symbolPathArray = saveState.getStrings("_SYMBOL_PATH", new String[0]);
|
||||
saveState.remove("_SYMBOL_PATH");
|
||||
if (symbolPathArray.length == 0) {
|
||||
return; // save state has no labels for program location.
|
||||
}
|
||||
|
||||
Address symbolAddress = (byteAddress != null) ? byteAddress : address;
|
||||
Address desiredSymbolAddress =
|
||||
(desiredByteAddress != null) ? desiredByteAddress : desiredAddress;
|
||||
if (symbolAddress == null || desiredSymbolAddress == null) {
|
||||
return; // no address match.
|
||||
return false; // no address match.
|
||||
}
|
||||
Symbol[] symbols = program.getSymbolTable().getSymbols(symbolAddress);
|
||||
if (symbols.length == 0) {
|
||||
return; // no symbols in program for matching.
|
||||
return false; // no symbols in program for matching.
|
||||
}
|
||||
Symbol[] desiredSymbols = desiredProgram.getSymbolTable().getSymbols(desiredSymbolAddress);
|
||||
if (desiredSymbols.length == 0) {
|
||||
return; // no symbols in desiredProgram for matching.
|
||||
return false; // no symbols in desiredProgram for matching.
|
||||
}
|
||||
|
||||
int desiredRow = adjustSymbolRow(saveState, symbols, desiredSymbols);
|
||||
|
||||
int desiredIndex = getDesiredSymbolIndex(desiredSymbols, desiredRow);
|
||||
|
||||
// Now get the desired symbol.
|
||||
|
@ -219,6 +221,7 @@ public class ProgramLocationTranslator {
|
|||
SymbolPath symbolPath = getSymbolPath(desiredSymbol);
|
||||
// Set symbol path for desiredProgram in the save state.
|
||||
saveState.putStrings("_SYMBOL_PATH", symbolPath.asArray());
|
||||
return true;
|
||||
}
|
||||
|
||||
private int adjustSymbolRow(SaveState saveState, Symbol[] symbols, Symbol[] desiredSymbols) {
|
||||
|
|
|
@ -37,8 +37,8 @@ import ghidra.app.plugin.core.functioncompare.actions.*;
|
|||
import ghidra.app.util.ListingHighlightProvider;
|
||||
import ghidra.app.util.viewer.format.*;
|
||||
import ghidra.app.util.viewer.listingpanel.*;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanelActionContext;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonViewActionContext;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonView;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
|
@ -57,12 +57,10 @@ import ghidra.util.task.TaskMonitor;
|
|||
import help.Help;
|
||||
|
||||
/**
|
||||
* Panel that displays two listings for comparison.
|
||||
* UI that displays two listings for comparison.
|
||||
*/
|
||||
|
||||
public class ListingCodeComparisonPanel
|
||||
extends CodeComparisonPanel implements
|
||||
FormatModelListener, OptionsChangeListener {
|
||||
public class ListingCodeComparisonView
|
||||
extends CodeComparisonView implements FormatModelListener, OptionsChangeListener {
|
||||
|
||||
public static final String NAME = "Listing View";
|
||||
private static final String DIFF_NAVIGATE_GROUP = "A2_DiffNavigate";
|
||||
|
@ -87,7 +85,7 @@ public class ListingCodeComparisonPanel
|
|||
|
||||
private ListingAddressCorrelation addressCorrelator;
|
||||
private ListingDiff listingDiff;
|
||||
private ListingCoordinator coordinator;
|
||||
private ListingDisplaySynchronizer coordinator;
|
||||
private boolean listingsLocked;
|
||||
|
||||
private ListingDiffActionManager diffActionManager;
|
||||
|
@ -107,7 +105,7 @@ public class ListingCodeComparisonPanel
|
|||
* @param owner the owner of this panel
|
||||
* @param tool the tool displaying this panel
|
||||
*/
|
||||
public ListingCodeComparisonPanel(String owner, PluginTool tool) {
|
||||
public ListingCodeComparisonView(String owner, PluginTool tool) {
|
||||
super(owner, tool);
|
||||
Help.getHelpService().registerHelp(this, new HelpLocation(HELP_TOPIC, "Listing_View"));
|
||||
initializeOptions();
|
||||
|
@ -450,7 +448,7 @@ public class ListingCodeComparisonPanel
|
|||
.description("Show the tool options for the Listing Code Comparison.")
|
||||
.popupMenuPath("Properties")
|
||||
.helpLocation(new HelpLocation(HELP_TOPIC, "Listing_Code_Comparison_Options"))
|
||||
.validContextWhen(c -> isValidPanelContext(c))
|
||||
.validWhen(c -> isValidPanelContext(c))
|
||||
.enabledWhen(c -> isShowing() && listingDiff.hasCorrelation())
|
||||
.onAction(c -> showOptionsDialog())
|
||||
.build();
|
||||
|
@ -497,10 +495,10 @@ public class ListingCodeComparisonPanel
|
|||
}
|
||||
|
||||
private boolean isValidPanelContext(ActionContext context) {
|
||||
if (!(context instanceof CodeComparisonPanelActionContext comparisonContext)) {
|
||||
if (!(context instanceof CodeComparisonViewActionContext comparisonContext)) {
|
||||
return false;
|
||||
}
|
||||
CodeComparisonPanel comparisonPanel = comparisonContext.getCodeComparisonPanel();
|
||||
CodeComparisonView comparisonPanel = comparisonContext.getCodeComparisonView();
|
||||
return comparisonPanel == this;
|
||||
}
|
||||
|
||||
|
@ -521,7 +519,7 @@ public class ListingCodeComparisonPanel
|
|||
coordinator = null;
|
||||
}
|
||||
if (listingsLocked) {
|
||||
coordinator = new ListingCoordinator(displays, addressCorrelator);
|
||||
coordinator = new ListingDisplaySynchronizer(displays, addressCorrelator);
|
||||
coordinator.sync(activeSide);
|
||||
}
|
||||
}
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -23,24 +23,24 @@ import ghidra.features.base.codecompare.panel.CodeComparisonActionContext;
|
|||
*/
|
||||
public class ListingComparisonActionContext extends CodeComparisonActionContext {
|
||||
|
||||
private ListingCodeComparisonPanel codeComparisonPanel = null;
|
||||
private ListingCodeComparisonView codeComparisonPanel = null;
|
||||
|
||||
/**
|
||||
* Constructor for a dual listing's action context.
|
||||
* @param provider the provider that uses this action context.
|
||||
* @param panel the ListingCodeComparisonPanel that generated this context
|
||||
*/
|
||||
public ListingComparisonActionContext(ComponentProvider provider, ListingCodeComparisonPanel panel) {
|
||||
public ListingComparisonActionContext(ComponentProvider provider, ListingCodeComparisonView panel) {
|
||||
super(provider, panel, panel.getActiveListingPanel().getFieldPanel());
|
||||
this.codeComparisonPanel = panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ListingCodeComparisonPanel} that generated this context
|
||||
* Returns the {@link ListingCodeComparisonView} that generated this context
|
||||
* @return the listing comparison panel that generated this context
|
||||
*/
|
||||
@Override
|
||||
public ListingCodeComparisonPanel getCodeComparisonPanel() {
|
||||
public ListingCodeComparisonView getCodeComparisonView() {
|
||||
return codeComparisonPanel;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -151,7 +151,7 @@ public class ListingDiffActionManager {
|
|||
ToggleIgnoreRegisterNamesAction() {
|
||||
super("Toggle Ignore Register Names", "DualListing");
|
||||
setDescription(HTMLUtilities.toHTML(
|
||||
"If selected, difference highlights should\n" + "ignore operand Registers."));
|
||||
"If selected, difference highlights should\nignore operand Registers."));
|
||||
setEnabled(true);
|
||||
setPopupMenuData(new MenuData(
|
||||
new String[] { "Ignore Operand Registers As Differences" },
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -20,8 +20,8 @@ import static ghidra.util.datastruct.Duo.Side.*;
|
|||
import java.math.BigInteger;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.internal.LayoutLockedFieldPanelCoordinator;
|
||||
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelCoordinator;
|
||||
import docking.widgets.fieldpanel.internal.LayoutLockedFieldPanelScrollCoordinator;
|
||||
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelScrollCoordinator;
|
||||
import docking.widgets.fieldpanel.support.ViewerPosition;
|
||||
import ghidra.app.util.viewer.listingpanel.ProgramLocationTranslator;
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
|
@ -34,23 +34,23 @@ import ghidra.util.datastruct.Duo.Side;
|
|||
/**
|
||||
* Keeps two listing panels synchronized, both the view and cursor location
|
||||
*/
|
||||
public class ListingCoordinator {
|
||||
class ListingDisplaySynchronizer {
|
||||
private Duo<ListingDisplay> displays;
|
||||
private Duo<Address> lockLineAddresses = new Duo<>();
|
||||
|
||||
private ProgramLocationTranslator locationTranslator;
|
||||
private LineLockedFieldPanelCoordinator viewCoordinator;
|
||||
private LineLockedFieldPanelScrollCoordinator viewCoordinator;
|
||||
|
||||
ListingCoordinator(Duo<ListingDisplay> displays, ListingAddressCorrelation correlator) {
|
||||
ListingDisplaySynchronizer(Duo<ListingDisplay> displays,
|
||||
ListingAddressCorrelation correlation) {
|
||||
this.displays = displays;
|
||||
this.locationTranslator = new ProgramLocationTranslator(correlator);
|
||||
this.locationTranslator = new ProgramLocationTranslator(correlation);
|
||||
FieldPanel left = displays.get(LEFT).getListingPanel().getFieldPanel();
|
||||
FieldPanel right = displays.get(RIGHT).getListingPanel().getFieldPanel();
|
||||
viewCoordinator = new LayoutLockedFieldPanelCoordinator(left, right);
|
||||
viewCoordinator = new LayoutLockedFieldPanelScrollCoordinator(left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* notification that the given side change to the given location
|
||||
* Notification that the given side change to the given location
|
||||
* @param side the side that changed
|
||||
* @param location the location from the given side
|
||||
*/
|
||||
|
@ -63,9 +63,7 @@ public class ListingCoordinator {
|
|||
if (otherLocation != null) {
|
||||
updateViewCoordinator(side, location, otherLocation);
|
||||
displays.get(otherSide).goTo(otherLocation);
|
||||
displays.get(side.otherSide()).updateCursorMarkers(otherLocation);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
|
@ -73,7 +71,7 @@ public class ListingCoordinator {
|
|||
}
|
||||
|
||||
/**
|
||||
* synchronized the two listings using the given side as the source
|
||||
* Synchronize the two listings using the given side as the source
|
||||
* @param side to synchronize from
|
||||
*/
|
||||
void sync(Side side) {
|
||||
|
@ -109,7 +107,7 @@ public class ListingCoordinator {
|
|||
if (leftAddress == null || rightAddress == null) {
|
||||
return;
|
||||
}
|
||||
lockLineAddresses = new Duo<>(leftAddress, rightAddress);
|
||||
|
||||
AddressIndexMap leftMap = displays.get(LEFT).getListingPanel().getAddressIndexMap();
|
||||
AddressIndexMap rightMap = displays.get(RIGHT).getListingPanel().getAddressIndexMap();
|
||||
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -46,7 +46,7 @@ abstract class ListingDisplayToggleAction extends ToggleDockingAction {
|
|||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
Object contextObject = context.getContextObject();
|
||||
if (contextObject instanceof ListingCodeComparisonPanel) {
|
||||
if (contextObject instanceof ListingCodeComparisonView) {
|
||||
Object sourceObject = context.getSourceObject();
|
||||
return sourceObject instanceof FieldPanel;
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -23,19 +23,20 @@ import ghidra.program.model.listing.Function;
|
|||
import ghidra.util.datastruct.Duo.Side;
|
||||
|
||||
public abstract class CodeComparisonActionContext extends DefaultActionContext
|
||||
implements CodeComparisonPanelActionContext {
|
||||
private CodeComparisonPanel comparisonPanel;
|
||||
implements CodeComparisonViewActionContext {
|
||||
private CodeComparisonView comparisonProvider;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param provider the ComponentProvider containing the code comparison panel
|
||||
* @param panel the CodeComparisonPanel that generated this context
|
||||
* @param comparisonProvider the provider that generated this context
|
||||
* @param component the focusable component for associated with the comparison panel
|
||||
*/
|
||||
public CodeComparisonActionContext(ComponentProvider provider, CodeComparisonPanel panel,
|
||||
public CodeComparisonActionContext(ComponentProvider provider,
|
||||
CodeComparisonView comparisonProvider,
|
||||
Component component) {
|
||||
super(provider, panel, component);
|
||||
this.comparisonPanel = panel;
|
||||
super(provider, comparisonProvider, component);
|
||||
this.comparisonProvider = comparisonProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,8 +45,8 @@ public abstract class CodeComparisonActionContext extends DefaultActionContext
|
|||
* @return the function to get information from
|
||||
*/
|
||||
public Function getSourceFunction() {
|
||||
Side activeSide = comparisonPanel.getActiveSide();
|
||||
return comparisonPanel.getFunction(activeSide.otherSide());
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
return comparisonProvider.getFunction(activeSide.otherSide());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,7 +55,7 @@ public abstract class CodeComparisonActionContext extends DefaultActionContext
|
|||
* @return the function to apply information to
|
||||
*/
|
||||
public Function getTargetFunction() {
|
||||
Side activeSide = comparisonPanel.getActiveSide();
|
||||
return comparisonPanel.getFunction(activeSide);
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
return comparisonProvider.getFunction(activeSide);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import docking.ComponentProvider;
|
|||
import docking.action.*;
|
||||
import docking.widgets.TitledPanel;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
@ -41,17 +42,19 @@ import ghidra.util.classfinder.ClassSearcher;
|
|||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
import utility.function.Callback;
|
||||
|
||||
/**
|
||||
* The CodeComparisonPanel class should be extended by any class that is to be
|
||||
* The {@link CodeComparisonView} class should be extended by any class that is to be
|
||||
* discovered by the {@link FunctionComparisonPanel} class and included as a
|
||||
* form of comparing two sections of code within the same or different programs
|
||||
* <p>
|
||||
* NOTE: ALL CodeComparisonPanel CLASSES MUST END IN
|
||||
* <code>CodeComparisonPanel</code> so they are discoverable by the {@link ClassSearcher}
|
||||
* NOTE: ALL CodeComparisonView CLASSES MUST END IN
|
||||
* <code>CodeComparisonView</code> so they are discoverable by the {@link ClassSearcher}
|
||||
*/
|
||||
public abstract class CodeComparisonPanel extends JPanel
|
||||
public abstract class CodeComparisonView extends JPanel
|
||||
implements ExtensionPoint {
|
||||
|
||||
public static final String HELP_TOPIC = "FunctionComparison";
|
||||
private static final Color ACTIVE_BORDER_COLOR = Palette.getColor("lightpink");
|
||||
private static final int MINIMUM_PANEL_WIDTH = 50;
|
||||
|
@ -71,6 +74,7 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
private ToggleOrientationAction toggleOrientationAction;
|
||||
private JComponent northComponent;
|
||||
private boolean showTitles = true;
|
||||
private Callback orientationChangedCallback = Callback.dummy();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -78,7 +82,7 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
* @param owner the name of the owner of this component
|
||||
* @param tool the tool that contains the component
|
||||
*/
|
||||
protected CodeComparisonPanel(String owner, PluginTool tool) {
|
||||
protected CodeComparisonView(String owner, PluginTool tool) {
|
||||
this.owner = owner;
|
||||
this.tool = tool;
|
||||
toggleOrientationAction = new ToggleOrientationAction(getName());
|
||||
|
@ -91,6 +95,10 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
return tool;
|
||||
}
|
||||
|
||||
public void setSaveState(SaveState saveState) {
|
||||
// for subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a comparison of two ComparisonData objects
|
||||
*
|
||||
|
@ -163,7 +171,7 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
public abstract void dispose();
|
||||
|
||||
/**
|
||||
* Returns the context object which corresponds to the area of focus within this provider's
|
||||
* Returns the context object which corresponds to the area of focus within this view's
|
||||
* component. Null is returned when there is no context.
|
||||
* @param componentProvider the provider that includes this code comparison component.
|
||||
* @param event mouse event which corresponds to this request.
|
||||
|
@ -296,6 +304,12 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
: JSplitPane.VERTICAL_SPLIT;
|
||||
splitPane.setOrientation(orientation);
|
||||
splitPane.setDividerLocation(0.5);
|
||||
|
||||
orientationChangedCallback.call();
|
||||
}
|
||||
|
||||
public void setOrientationChangedCallback(Callback callback) {
|
||||
this.orientationChangedCallback = Callback.dummyIfNull(callback);
|
||||
}
|
||||
|
||||
private void setTitle(TitledPanel titlePanel, String titlePrefix, String title) {
|
||||
|
@ -343,7 +357,7 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
setActiveSide(LEFT);
|
||||
}
|
||||
|
||||
private void addMouseAndFocusListeners(Side side) {
|
||||
protected void addMouseAndFocusListeners(Side side) {
|
||||
JComponent comp = getComparisonComponent(side);
|
||||
comp.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
|
@ -374,6 +388,7 @@ public abstract class CodeComparisonPanel extends JPanel
|
|||
}
|
||||
|
||||
private void addMouseListenerRecursively(Component component, MouseListener listener) {
|
||||
component.removeMouseListener(listener);
|
||||
component.addMouseListener(listener);
|
||||
if (component instanceof Container container) {
|
||||
for (int i = 0; i < container.getComponentCount(); i++) {
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -16,14 +16,14 @@
|
|||
package ghidra.features.base.codecompare.panel;
|
||||
|
||||
/**
|
||||
* Action context for a CodeComparisonPanel.
|
||||
* Action context for a {@link CodeComparisonView}.
|
||||
*/
|
||||
public interface CodeComparisonPanelActionContext {
|
||||
public interface CodeComparisonViewActionContext {
|
||||
|
||||
/**
|
||||
* Gets the CodeComparisonPanel associated with this context.
|
||||
* @return the code comparison panel.
|
||||
* Gets the view associated with this context.
|
||||
* @return the code comparison provider.
|
||||
*/
|
||||
public abstract CodeComparisonPanel getCodeComparisonPanel();
|
||||
public abstract CodeComparisonView getCodeComparisonView();
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.base.codecompare.panel;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
||||
/**
|
||||
* A state object to save settings each type of comparison view known by the system. This class
|
||||
* is meant to be used to allow user settings to be applied to each new comparison widget that is
|
||||
* created. Also, the class allows the tool to save those settings when the tool is saved.
|
||||
* <p>
|
||||
* When a comparison provider updates its save state object, it should call
|
||||
* {@link PluginTool#setConfigChanged(boolean)} so that tool knows there are settings to be saved.
|
||||
*/
|
||||
public class CodeComparisonViewState {
|
||||
|
||||
private static final String FUNCTION_COMPARISON_STATES = "CodeComparisonStates";
|
||||
|
||||
private Map<Class<? extends CodeComparisonView>, SaveState> states = new HashMap<>();
|
||||
|
||||
public SaveState getSaveState(Class<? extends CodeComparisonView> clazz) {
|
||||
return states.computeIfAbsent(clazz, this::createSaveState);
|
||||
}
|
||||
|
||||
private SaveState createSaveState(Class<? extends CodeComparisonView> clazz) {
|
||||
return new SaveState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the tool to write the panels' saved states into the tools save state
|
||||
* @param saveState the tool's save state
|
||||
*/
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
Set<Entry<Class<? extends CodeComparisonView>, SaveState>> entries = states.entrySet();
|
||||
SaveState classStates = new SaveState();
|
||||
for (Entry<Class<? extends CodeComparisonView>, SaveState> entry : entries) {
|
||||
Class<? extends CodeComparisonView> clazz = entry.getKey();
|
||||
SaveState subState = entry.getValue();
|
||||
classStates.putSaveState(clazz.getName(), subState);
|
||||
}
|
||||
|
||||
saveState.putSaveState(FUNCTION_COMPARISON_STATES, classStates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the tool to load saved state for the comparison providers
|
||||
* @param saveState the tool's state
|
||||
*/
|
||||
public void readConfigState(SaveState saveState) {
|
||||
|
||||
SaveState classStates = saveState.getSaveState(FUNCTION_COMPARISON_STATES);
|
||||
if (classStates == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] names = classStates.getNames();
|
||||
for (String className : names) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends CodeComparisonView> clazz =
|
||||
(Class<? extends CodeComparisonView>) Class.forName(className);
|
||||
SaveState classState = classStates.getSaveState(className);
|
||||
states.put(clazz, classState);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ import ghidra.program.model.listing.Program;
|
|||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
/**
|
||||
* ComparisonData is an abstract of items that can be compared in a {@link CodeComparisonPanel}.
|
||||
* ComparisonData is an abstraction of items that can be compared in a {@link CodeComparisonView}.
|
||||
* Not all comparison panels can handle all types of comparison data. For example, the decompiler
|
||||
* comparison only works when the comparison data is a function.
|
||||
*/
|
||||
|
@ -71,6 +71,7 @@ public interface ComparisonData {
|
|||
|
||||
/**
|
||||
* Returns the initial program location to put the cursor when the panel is first displayed
|
||||
* @return the location
|
||||
*/
|
||||
public ProgramLocation getInitialLocation();
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -21,7 +21,6 @@ import static ghidra.util.datastruct.Duo.Side.*;
|
|||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -34,10 +33,9 @@ import docking.ComponentProvider;
|
|||
import docking.action.*;
|
||||
import docking.widgets.tabbedpane.DockingTabRenderer;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.listing.ListingCodeComparisonView;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
@ -48,18 +46,16 @@ import help.Help;
|
|||
import help.HelpService;
|
||||
|
||||
/**
|
||||
* A panel for displaying {@link Function functions}, {@link Data data}, or
|
||||
* {@link AddressSet address sets} side-by-side for comparison purposes
|
||||
* A panel for displaying {@link Function functions} side-by-side for comparison purposes
|
||||
*/
|
||||
public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
||||
private static final String ORIENTATION_PROPERTY_NAME = "ORIENTATION";
|
||||
|
||||
private static final String DEFAULT_CODE_COMPARISON_VIEW = ListingCodeComparisonPanel.NAME;
|
||||
private static final String DEFAULT_CODE_COMPARISON_VIEW = ListingCodeComparisonView.NAME;
|
||||
private static final String COMPARISON_VIEW_DISPLAYED = "COMPARISON_VIEW_DISPLAYED";
|
||||
private static final String CODE_COMPARISON_LOCK_SCROLLING_TOGETHER =
|
||||
"CODE_COMPARISON_LOCK_SCROLLING_TOGETHER";
|
||||
|
||||
private static final HelpService help = Help.getHelpService();
|
||||
private static final String HELP_TOPIC = "FunctionComparison";
|
||||
|
||||
private static final Icon SYNC_SCROLLING_ICON =
|
||||
|
@ -72,23 +68,40 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
|
||||
private JTabbedPane tabbedPane;
|
||||
private Map<String, JComponent> tabNameToComponentMap;
|
||||
private List<CodeComparisonPanel> codeComparisonPanels;
|
||||
private List<CodeComparisonView> codeComparisonViews;
|
||||
private ToggleScrollLockAction toggleScrollLockAction;
|
||||
private boolean syncScrolling = false;
|
||||
|
||||
private Duo<ComparisonData> comparisonData = new Duo<ComparisonData>();
|
||||
|
||||
public FunctionComparisonPanel(PluginTool tool, String owner) {
|
||||
this.comparisonData = new Duo<>(EMPTY, EMPTY);
|
||||
private FunctionComparisonState state;
|
||||
|
||||
codeComparisonPanels = getCodeComparisonPanels(tool, owner);
|
||||
/**
|
||||
* Constructor
|
||||
* @param tool the tool
|
||||
* @param owner the owner's name
|
||||
* @param state the comparison save state
|
||||
*/
|
||||
public FunctionComparisonPanel(PluginTool tool, String owner, FunctionComparisonState state) {
|
||||
this.comparisonData = new Duo<>(EMPTY, EMPTY);
|
||||
this.state = state;
|
||||
|
||||
state.addUpdateCallback(this::comparisonStateUpdated);
|
||||
|
||||
codeComparisonViews = getCodeComparisonViews(tool, owner);
|
||||
tabNameToComponentMap = new HashMap<>();
|
||||
createMainPanel();
|
||||
createActions(owner);
|
||||
setScrollingSyncState(true);
|
||||
HelpService help = Help.getHelpService();
|
||||
help.registerHelp(this, new HelpLocation(HELP_TOPIC, "Function Comparison"));
|
||||
}
|
||||
|
||||
private void comparisonStateUpdated() {
|
||||
readPanelState();
|
||||
readViewState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the given functions into the views of this panel
|
||||
*
|
||||
|
@ -118,9 +131,9 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
public void loadComparisons(ComparisonData left, ComparisonData right) {
|
||||
comparisonData = new Duo<>(left, right);
|
||||
|
||||
CodeComparisonPanel activePanel = getActiveComparisonPanel();
|
||||
if (activePanel != null) {
|
||||
activePanel.loadComparisons(left, right);
|
||||
CodeComparisonView activeView = getActiveComparisonView();
|
||||
if (activeView != null) {
|
||||
activeView.loadComparisons(left, right);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -173,9 +186,9 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
|
||||
// Setting the addresses to be displayed to null effectively clears
|
||||
// the display
|
||||
CodeComparisonPanel activePanel = getActiveComparisonPanel();
|
||||
if (activePanel != null) {
|
||||
activePanel.clearComparisons();
|
||||
CodeComparisonView activeView = getActiveComparisonView();
|
||||
if (activeView != null) {
|
||||
activeView.clearComparisons();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,15 +203,15 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the ListingCodeComparisonPanel being displayed by this panel
|
||||
* Gets the ListingCodeComparisonView being displayed by this panel
|
||||
* if one exists
|
||||
*
|
||||
* @return the comparison panel or null
|
||||
*/
|
||||
public ListingCodeComparisonPanel getDualListingPanel() {
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
if (codeComparisonPanel instanceof ListingCodeComparisonPanel listingPanel) {
|
||||
return listingPanel;
|
||||
public ListingCodeComparisonView getDualListingView() {
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
if (view instanceof ListingCodeComparisonView listingView) {
|
||||
return listingView;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -207,13 +220,14 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
tabChanged();
|
||||
writeTabState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current tabbed panel to be the component with the given name
|
||||
*
|
||||
* @param name name of view to set as the current tab
|
||||
* @return true if the named view was found in the provider map
|
||||
* @return true if the named view was found in the view map
|
||||
*/
|
||||
public boolean setCurrentTabbedComponent(String name) {
|
||||
|
||||
|
@ -255,26 +269,34 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
tabbedPane.removeAll();
|
||||
|
||||
setVisible(false);
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
codeComparisonPanel.dispose();
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
view.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void programClosed(Program program) {
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
codeComparisonPanel.programClosed(program);
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
view.programClosed(program);
|
||||
}
|
||||
}
|
||||
|
||||
public CodeComparisonPanel getCodeComparisonPanelByName(String name) {
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
if (name.equals(codeComparisonPanel.getName())) {
|
||||
return codeComparisonPanel;
|
||||
public CodeComparisonView getCodeComparisonView(String name) {
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
if (name.equals(view.getName())) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void selectComparisonView(String name) {
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
if (name.equals(view.getName())) {
|
||||
tabbedPane.setSelectedComponent(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the main tabbed panel
|
||||
*/
|
||||
|
@ -287,22 +309,21 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
add(tabbedPane, BorderLayout.CENTER);
|
||||
setPreferredSize(new Dimension(200, 300));
|
||||
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
tabbedPane.add(codeComparisonPanel.getName(), codeComparisonPanel);
|
||||
tabNameToComponentMap.put(codeComparisonPanel.getName(), codeComparisonPanel);
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
tabbedPane.add(view.getName(), view);
|
||||
tabNameToComponentMap.put(view.getName(), view);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when there is a tab change. This loads the active tab with
|
||||
* the appropriate data to be compared.
|
||||
* Invoked when there is a tab change. This loads the active tab with the data to be compared.
|
||||
*/
|
||||
private void tabChanged() {
|
||||
CodeComparisonPanel activePanel = getActiveComparisonPanel();
|
||||
if (activePanel == null) {
|
||||
CodeComparisonView activeView = getActiveComparisonView();
|
||||
if (activeView == null) {
|
||||
return; // initializing
|
||||
}
|
||||
activePanel.loadComparisons(comparisonData.get(LEFT), comparisonData.get(RIGHT));
|
||||
activeView.loadComparisons(comparisonData.get(LEFT), comparisonData.get(RIGHT));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -311,70 +332,73 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
* @return the currently selected comparison panel, or null if nothing
|
||||
* selected
|
||||
*/
|
||||
private CodeComparisonPanel getActiveComparisonPanel() {
|
||||
return (CodeComparisonPanel) tabbedPane.getSelectedComponent();
|
||||
private CodeComparisonView getActiveComparisonView() {
|
||||
return (CodeComparisonView) tabbedPane.getSelectedComponent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the FunctionComparisonPanel and which CodeComparisonPanel is currently
|
||||
* displayed based on the specified saveState
|
||||
*
|
||||
* @param prefix identifier to prepend to any save state names to make them unique
|
||||
* @param saveState the save state for retrieving information
|
||||
*/
|
||||
public void readConfigState(String prefix, SaveState saveState) {
|
||||
private void readViewState() {
|
||||
CodeComparisonViewState viewState = state.getViewState();
|
||||
codeComparisonViews.forEach(v -> {
|
||||
Class<? extends CodeComparisonView> viewClass = v.getClass();
|
||||
SaveState saveState = viewState.getSaveState(viewClass);
|
||||
v.setSaveState(saveState);
|
||||
});
|
||||
}
|
||||
|
||||
private void readPanelState() {
|
||||
|
||||
SaveState panelState = state.getPanelState();
|
||||
String currentTabView =
|
||||
saveState.getString(prefix + COMPARISON_VIEW_DISPLAYED, DEFAULT_CODE_COMPARISON_VIEW);
|
||||
panelState.getString(COMPARISON_VIEW_DISPLAYED, DEFAULT_CODE_COMPARISON_VIEW);
|
||||
setCurrentTabbedComponent(currentTabView);
|
||||
setScrollingSyncState(
|
||||
saveState.getBoolean(prefix + CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, true));
|
||||
panelState.getBoolean(CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, true));
|
||||
|
||||
for (CodeComparisonPanel panel : codeComparisonPanels) {
|
||||
String key = prefix + panel.getName() + ORIENTATION_PROPERTY_NAME;
|
||||
panel.setSideBySide(saveState.getBoolean(key, true));
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
String key = view.getName() + ORIENTATION_PROPERTY_NAME;
|
||||
view.setSideBySide(panelState.getBoolean(key, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the information to the save state about the FunctionComparisonPanel and
|
||||
* which CodeComparisonPanel is currently displayed
|
||||
*
|
||||
* @param prefix identifier to prepend to any save state names to make them unique
|
||||
* @param saveState the save state where the information gets written
|
||||
*/
|
||||
public void writeConfigState(String prefix, SaveState saveState) {
|
||||
private void writeTabState() {
|
||||
String currentComponentName = getCurrentComponentName();
|
||||
if (currentComponentName != null) {
|
||||
saveState.putString(prefix + COMPARISON_VIEW_DISPLAYED, getCurrentComponentName());
|
||||
}
|
||||
saveState.putBoolean(prefix + CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, isScrollingSynced());
|
||||
|
||||
for (CodeComparisonPanel panel : codeComparisonPanels) {
|
||||
String key = prefix + panel.getName() + ORIENTATION_PROPERTY_NAME;
|
||||
boolean sideBySide = panel.isSideBySide();
|
||||
saveState.putBoolean(key, sideBySide);
|
||||
if (currentComponentName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SaveState panelState = state.getPanelState();
|
||||
panelState.putString(COMPARISON_VIEW_DISPLAYED, getCurrentComponentName());
|
||||
state.setChanged();
|
||||
}
|
||||
|
||||
private void writeScrollState() {
|
||||
SaveState panelState = state.getPanelState();
|
||||
panelState.putBoolean(CODE_COMPARISON_LOCK_SCROLLING_TOGETHER, isScrollingSynced());
|
||||
state.setChanged();
|
||||
}
|
||||
|
||||
private void writeOrientationState() {
|
||||
|
||||
SaveState panelState = state.getPanelState();
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
String key = view.getName() + ORIENTATION_PROPERTY_NAME;
|
||||
boolean sideBySide = view.isSideBySide();
|
||||
panelState.putBoolean(key, sideBySide);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all actions for the FunctionComparisonPanel and all CodeComparisonPanels in this
|
||||
* FunctionComparisonPanel
|
||||
*
|
||||
* @return the code comparison actions
|
||||
*/
|
||||
public DockingAction[] getCodeComparisonActions() {
|
||||
ArrayList<DockingAction> dockingActionList = new ArrayList<>();
|
||||
|
||||
// Get actions for this functionComparisonPanel
|
||||
DockingAction[] functionComparisonActions = getActions();
|
||||
for (DockingAction dockingAction : functionComparisonActions) {
|
||||
dockingActionList.add(dockingAction);
|
||||
// Get actions for this panel
|
||||
DockingAction[] actions = getActions();
|
||||
for (DockingAction action : actions) {
|
||||
dockingActionList.add(action);
|
||||
}
|
||||
|
||||
// Get actions for each CodeComparisonPanel
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
dockingActionList.addAll(codeComparisonPanel.getActions());
|
||||
// Get actions for each view
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
dockingActionList.addAll(view.getActions());
|
||||
}
|
||||
|
||||
return dockingActionList.toArray(new DockingAction[dockingActionList.size()]);
|
||||
|
@ -382,7 +406,7 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
|
||||
/**
|
||||
* Sets the prefixes that are to be prepended to the title displayed for each side of
|
||||
* each CodeComparisonPanel
|
||||
* each {@link CodeComparisonView}
|
||||
*
|
||||
* @param leftTitlePrefix the prefix to prepend to the left titles
|
||||
* @param rightTitlePrefix the prefix to prepend to the right titles
|
||||
|
@ -390,8 +414,8 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
public void setTitlePrefixes(String leftTitlePrefix, String rightTitlePrefix) {
|
||||
Component[] components = tabbedPane.getComponents();
|
||||
for (Component component : components) {
|
||||
if (component instanceof CodeComparisonPanel) {
|
||||
((CodeComparisonPanel) component).setTitlePrefixes(leftTitlePrefix,
|
||||
if (component instanceof CodeComparisonView) {
|
||||
((CodeComparisonView) component).setTitlePrefixes(leftTitlePrefix,
|
||||
rightTitlePrefix);
|
||||
}
|
||||
}
|
||||
|
@ -405,9 +429,9 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
* @return the action context
|
||||
*/
|
||||
public ActionContext getActionContext(MouseEvent event, ComponentProvider componentProvider) {
|
||||
CodeComparisonPanel activePanel = getDisplayedPanel();
|
||||
if (activePanel != null) {
|
||||
return activePanel.getActionContext(componentProvider, event);
|
||||
CodeComparisonView activeProvider = getDisplayedView();
|
||||
if (activeProvider != null) {
|
||||
return activeProvider.getActionContext(componentProvider, event);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -432,43 +456,46 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
if (isScrollingSynced() == syncScrolling) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggleScrollLockAction.setSelected(syncScrolling);
|
||||
toggleScrollLockAction.setToolBarData(new ToolBarData(
|
||||
syncScrolling ? SYNC_SCROLLING_ICON : UNSYNC_SCROLLING_ICON, SCROLLING_GROUP));
|
||||
// Notify each comparison panel of the scrolling sync state.
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
codeComparisonPanel.setSynchronizedScrolling(syncScrolling);
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
view.setSynchronizedScrolling(syncScrolling);
|
||||
}
|
||||
this.syncScrolling = syncScrolling;
|
||||
|
||||
writeScrollState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently displayed CodeComparisonPanel
|
||||
* Gets the currently displayed {@link CodeComparisonView}
|
||||
*
|
||||
* @return the current panel or null.
|
||||
*/
|
||||
public CodeComparisonPanel getDisplayedPanel() {
|
||||
public CodeComparisonView getDisplayedView() {
|
||||
int selectedIndex = tabbedPane.getSelectedIndex();
|
||||
Component component = tabbedPane.getComponentAt(selectedIndex);
|
||||
return (CodeComparisonPanel) component;
|
||||
return (CodeComparisonView) component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the enablement for all actions provided by each panel
|
||||
* Updates the enablement for all actions provided by each view
|
||||
*/
|
||||
public void updateActionEnablement() {
|
||||
for (CodeComparisonPanel codeComparisonPanel : codeComparisonPanels) {
|
||||
codeComparisonPanel.updateActionEnablement();
|
||||
for (CodeComparisonView view : codeComparisonViews) {
|
||||
view.updateActionEnablement();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current code comparison panel being viewed
|
||||
* Get the current code comparison view being viewed
|
||||
*
|
||||
* @return null if there is no code comparison panel
|
||||
* @return null if there is no code comparison view
|
||||
*/
|
||||
public CodeComparisonPanel getCurrentComponent() {
|
||||
return (CodeComparisonPanel) tabbedPane.getSelectedComponent();
|
||||
public CodeComparisonView getCurrentView() {
|
||||
return (CodeComparisonView) tabbedPane.getSelectedComponent();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -519,45 +546,48 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
|
|||
}
|
||||
}
|
||||
|
||||
public List<CodeComparisonPanel> getComparisonPanels() {
|
||||
return codeComparisonPanels;
|
||||
public List<CodeComparisonView> getComparisonView() {
|
||||
return codeComparisonViews;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discovers the CodeComparisonPanels which are extension points
|
||||
* Discovers the {@link CodeComparisonView}s which are extension points
|
||||
*
|
||||
* @return the CodeComparisonPanels which are extension points
|
||||
* @return the views which are extension points
|
||||
*/
|
||||
private List<CodeComparisonPanel> getCodeComparisonPanels(PluginTool tool, String owner) {
|
||||
if (codeComparisonPanels == null) {
|
||||
codeComparisonPanels = createAllPossibleCodeComparisonPanels(tool, owner);
|
||||
codeComparisonPanels.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));
|
||||
private List<CodeComparisonView> getCodeComparisonViews(PluginTool tool, String owner) {
|
||||
if (codeComparisonViews == null) {
|
||||
codeComparisonViews = createAllCodeComparisonViews(tool, owner);
|
||||
codeComparisonViews.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));
|
||||
}
|
||||
return codeComparisonPanels;
|
||||
return codeComparisonViews;
|
||||
}
|
||||
|
||||
private List<CodeComparisonPanel> createAllPossibleCodeComparisonPanels(PluginTool tool,
|
||||
private List<CodeComparisonView> createAllCodeComparisonViews(PluginTool tool,
|
||||
String owner) {
|
||||
|
||||
List<CodeComparisonPanel> instances = new ArrayList<>();
|
||||
|
||||
List<Class<? extends CodeComparisonPanel>> classes =
|
||||
ClassSearcher.getClasses(CodeComparisonPanel.class);
|
||||
|
||||
for (Class<? extends CodeComparisonPanel> panelClass : classes) {
|
||||
CodeComparisonViewState viewState = state.getViewState();
|
||||
List<CodeComparisonView> instances = new ArrayList<>();
|
||||
List<Class<? extends CodeComparisonView>> classes =
|
||||
ClassSearcher.getClasses(CodeComparisonView.class);
|
||||
for (Class<? extends CodeComparisonView> viewClass : classes) {
|
||||
try {
|
||||
Constructor<? extends CodeComparisonPanel> constructor =
|
||||
panelClass.getConstructor(String.class, PluginTool.class);
|
||||
CodeComparisonPanel panel = constructor.newInstance(owner, tool);
|
||||
instances.add(panel);
|
||||
Constructor<? extends CodeComparisonView> constructor =
|
||||
viewClass.getConstructor(String.class, PluginTool.class);
|
||||
CodeComparisonView view = constructor.newInstance(owner, tool);
|
||||
|
||||
SaveState saveState = viewState.getSaveState(viewClass);
|
||||
view.setSaveState(saveState);
|
||||
|
||||
view.setOrientationChangedCallback(() -> writeOrientationState());
|
||||
|
||||
instances.add(view);
|
||||
}
|
||||
catch (NoSuchMethodException | SecurityException | InstantiationException
|
||||
| IllegalAccessException | IllegalArgumentException
|
||||
| InvocationTargetException e) {
|
||||
catch (Exception e) {
|
||||
Msg.showError(this, null, "Error Creating Extension Point",
|
||||
"Error creating class " + panelClass.getName() +
|
||||
"Error creating class " + viewClass.getName() +
|
||||
" when creating extension points for " +
|
||||
CodeComparisonPanel.class.getName(),
|
||||
CodeComparisonView.class.getName(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.base.codecompare.panel;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import utility.function.Callback;
|
||||
|
||||
/**
|
||||
* An object to share config state between providers and all views within those providers.
|
||||
* <p>
|
||||
* When a comparison provider updates its save state object, it should call
|
||||
* {@link PluginTool#setConfigChanged(boolean)} so that tool knows there are settings to be saved.
|
||||
*/
|
||||
public class FunctionComparisonState {
|
||||
|
||||
private static final String PROVIDER_SAVE_STATE_NAME = "FunctionComparison";
|
||||
|
||||
private SaveState panelState = new SaveState();
|
||||
private CodeComparisonViewState comparisonState = new CodeComparisonViewState();
|
||||
|
||||
private PluginTool tool;
|
||||
|
||||
private List<Callback> updateCallbacks = new ArrayList<>();
|
||||
|
||||
public FunctionComparisonState(PluginTool tool) {
|
||||
this.tool = tool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state object for the provider
|
||||
* @return the state object for the provider
|
||||
*/
|
||||
public SaveState getPanelState() {
|
||||
return panelState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the save state object for the views that live inside a provider
|
||||
* @return the state
|
||||
*/
|
||||
public CodeComparisonViewState getViewState() {
|
||||
return comparisonState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals to the tool that there are changes to the config state that can be saved.
|
||||
*/
|
||||
public void setChanged() {
|
||||
tool.setConfigChanged(true);
|
||||
}
|
||||
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
saveState.putSaveState(PROVIDER_SAVE_STATE_NAME, panelState);
|
||||
comparisonState.writeConfigState(saveState);
|
||||
}
|
||||
|
||||
public void readConfigState(SaveState saveState) {
|
||||
SaveState restoredPanelState = saveState.getSaveState(PROVIDER_SAVE_STATE_NAME);
|
||||
if (restoredPanelState != null) {
|
||||
panelState = restoredPanelState;
|
||||
}
|
||||
|
||||
comparisonState.readConfigState(saveState);
|
||||
updateCallbacks.forEach(Callback::call);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a callback to this state that is notified when this state changes.
|
||||
* @param callback the callback
|
||||
*/
|
||||
public void addUpdateCallback(Callback callback) {
|
||||
updateCallbacks.add(Objects.requireNonNull(callback));
|
||||
}
|
||||
}
|
|
@ -304,7 +304,7 @@ class MemorySearchControlPanel extends JPanel {
|
|||
return;
|
||||
}
|
||||
|
||||
DockingUtils.setTipWindowEnabled(false);
|
||||
setMyToolTipsEnabled(false);
|
||||
|
||||
Point location = searchInputField.getLocation();
|
||||
adjustLocationForCaretPosition(location);
|
||||
|
@ -326,7 +326,7 @@ class MemorySearchControlPanel extends JPanel {
|
|||
|
||||
private void clearInputError() {
|
||||
errorMessage = null;
|
||||
DockingUtils.setTipWindowEnabled(true);
|
||||
setMyToolTipsEnabled(true);
|
||||
PopupWindow.hideAllWindows();
|
||||
if (popup != null) {
|
||||
popup.dispose();
|
||||
|
@ -336,6 +336,34 @@ class MemorySearchControlPanel extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
private void setMyToolTipsEnabled(boolean enabled) {
|
||||
|
||||
if (!DockingUtils.isTipWindowEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ToolTipManager ttm = ToolTipManager.sharedInstance();
|
||||
doSetMyToolTipsEnabled(this, ttm, enabled);
|
||||
}
|
||||
|
||||
private void doSetMyToolTipsEnabled(JComponent c, ToolTipManager ttm, boolean enabled) {
|
||||
|
||||
if (enabled) {
|
||||
ttm.registerComponent(c);
|
||||
}
|
||||
else {
|
||||
ttm.unregisterComponent(c);
|
||||
}
|
||||
|
||||
Component[] children = c.getComponents();
|
||||
for (Component child : children) {
|
||||
if (child instanceof JComponent) {
|
||||
doSetMyToolTipsEnabled((JComponent) child, ttm, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateCombo() {
|
||||
ByteMatcher[] historyArray = searchHistory.getHistoryAsArray();
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ public class ProgramSelection implements AddressSetView {
|
|||
* @param addressFactory NOT USED
|
||||
* @param from the start of the selection
|
||||
* @param to the end of the selection
|
||||
* @deprecated use {@link #ProgramSelection(Address, Address)}
|
||||
*/
|
||||
@Deprecated(since = "11.2", forRemoval = true)
|
||||
public ProgramSelection(AddressFactory addressFactory, Address from, Address to) {
|
||||
|
|
|
@ -55,7 +55,8 @@ import ghidra.program.model.lang.*;
|
|||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.program.util.ProgramUtilities;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.TaskUtilities;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.*;
|
||||
|
@ -75,7 +76,7 @@ public class TestEnv {
|
|||
private static Set<TestEnv> instances = new HashSet<>();
|
||||
|
||||
private FrontEndTool frontEndTool;
|
||||
private PluginTool tool;
|
||||
protected PluginTool tool;
|
||||
|
||||
private static TestProgramManager programManager = new TestProgramManager();
|
||||
|
||||
|
@ -962,7 +963,7 @@ public class TestEnv {
|
|||
|
||||
public Program loadResourceProgramAsBinary(String programName, Language language,
|
||||
CompilerSpec compilerSpec) throws LanguageNotFoundException, IOException,
|
||||
CancelledException, DuplicateNameException, InvalidNameException, VersionException {
|
||||
CancelledException, VersionException {
|
||||
File file = AbstractGenericTest.getTestDataFile(programName);
|
||||
if (file == null || !file.exists()) {
|
||||
throw new FileNotFoundException("Can not find test program: " + programName);
|
||||
|
@ -971,8 +972,7 @@ public class TestEnv {
|
|||
}
|
||||
|
||||
public Program loadResourceProgramAsBinary(String programName, Processor processor)
|
||||
throws CancelledException, DuplicateNameException, InvalidNameException,
|
||||
VersionException, IOException {
|
||||
throws CancelledException, VersionException, IOException {
|
||||
Language language =
|
||||
DefaultLanguageService.getLanguageService().getDefaultLanguage(processor);
|
||||
CompilerSpec compilerSpec = language.getDefaultCompilerSpec();
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -41,7 +41,7 @@ public class NamespaceTableColumn
|
|||
return parentPath == null ? GlobalNamespace.GLOBAL_NAMESPACE_NAME : parentPath;
|
||||
}
|
||||
Symbol symbol = getSymbol(rowObject, program);
|
||||
if (symbol != null) {
|
||||
if (symbol != null && !symbol.isDeleted() && !symbol.isDynamic()) {
|
||||
return symbol.getParentNamespace().getName(true);
|
||||
}
|
||||
Function function =
|
||||
|
|
|
@ -87,7 +87,7 @@ import resources.ResourceManager;
|
|||
|
||||
public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
private static final String SCREENSHOT_USER_NAME = "User-1";
|
||||
protected static final String SCREENSHOT_USER_NAME = "User-1";
|
||||
|
||||
static {
|
||||
System.setProperty("user.name", "User-1");
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -21,6 +21,9 @@ import org.jdom.Element;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.xml.XmlUtilities;
|
||||
|
||||
public class SaveStateTest {
|
||||
private SaveState saveState;
|
||||
|
||||
|
@ -46,6 +49,16 @@ public class SaveStateTest {
|
|||
assertEquals(2, restoreSubState.getNames().length);
|
||||
assertEquals(5, restoreSubState.getInt("a", 0));
|
||||
assertEquals("bar", restoreSubState.getString("foo", ""));
|
||||
|
||||
SaveState s1 = new SaveState("Parent");
|
||||
SaveState c1 = new SaveState("Child1");
|
||||
c1.putBoolean("Bool1", false);
|
||||
c1.putString("String1", "Hey bob");
|
||||
s1.putSaveState("MapChildName1", c1);
|
||||
Element e = s1.saveToXml();
|
||||
String s = XmlUtilities.toString(e);
|
||||
Msg.debug(this, s);
|
||||
|
||||
}
|
||||
|
||||
private SaveState saveAndRestoreToXml() throws Exception {
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -23,4 +23,5 @@ eclipse.project.name = 'Features CodeCompare'
|
|||
|
||||
dependencies {
|
||||
api project(":Decompiler")
|
||||
api project(":FunctionGraph")
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import ghidra.util.Msg;
|
|||
|
||||
/**
|
||||
* Subclass of {@link AbstractMatchedTokensAction} for actions in a
|
||||
* {@link DecompilerCodeComparisonPanel} that are available only when the matched tokens are
|
||||
* {@link DecompilerCodeComparisonView} that are available only when the matched tokens are
|
||||
* function calls
|
||||
*/
|
||||
public abstract class AbstractMatchedCalleeTokensAction extends AbstractMatchedTokensAction {
|
||||
|
@ -37,12 +37,12 @@ public abstract class AbstractMatchedCalleeTokensAction extends AbstractMatchedT
|
|||
*
|
||||
* @param actionName name of action
|
||||
* @param owner owner of action
|
||||
* @param diffPanel diff panel containing action
|
||||
* @param comparisonProvider diff comparison provider containing action
|
||||
* @param disableOnReadOnly if true, action will be disabled for read-only programs
|
||||
*/
|
||||
public AbstractMatchedCalleeTokensAction(String actionName, String owner,
|
||||
DecompilerCodeComparisonPanel diffPanel, boolean disableOnReadOnly) {
|
||||
super(actionName, owner, diffPanel, disableOnReadOnly);
|
||||
DecompilerCodeComparisonView comparisonProvider, boolean disableOnReadOnly) {
|
||||
super(actionName, owner, comparisonProvider, disableOnReadOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,15 +69,15 @@ public abstract class AbstractMatchedCalleeTokensAction extends AbstractMatchedT
|
|||
|
||||
@Override
|
||||
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||
DecompilerCodeComparisonPanel decompPanel = context.getCodeComparisonPanel();
|
||||
DecompilerCodeComparisonView provider = context.getCodeComparisonView();
|
||||
|
||||
TokenPair currentPair = context.getTokenPair();
|
||||
|
||||
ClangFuncNameToken leftFuncToken = (ClangFuncNameToken) currentPair.leftToken();
|
||||
ClangFuncNameToken rightFuncToken = (ClangFuncNameToken) currentPair.rightToken();
|
||||
|
||||
Function leftFunction = getFuncFromToken(leftFuncToken, decompPanel.getProgram(LEFT));
|
||||
Function rightFunction = getFuncFromToken(rightFuncToken, decompPanel.getProgram(RIGHT));
|
||||
Function leftFunction = getFuncFromToken(leftFuncToken, provider.getProgram(LEFT));
|
||||
Function rightFunction = getFuncFromToken(rightFuncToken, provider.getProgram(RIGHT));
|
||||
if (leftFunction == null || rightFunction == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -19,13 +19,13 @@ import docking.ActionContext;
|
|||
import docking.action.DockingAction;
|
||||
|
||||
/**
|
||||
* This is a base class for actions in a {@link DecompilerCodeComparisonPanel}
|
||||
* This is a base class for actions in a {@link DecompilerCodeComparisonView}
|
||||
*/
|
||||
public abstract class AbstractMatchedTokensAction extends DockingAction {
|
||||
protected static final String MENU_PARENT = "Apply From Other Function";
|
||||
protected static final String HELP_TOPIC = "FunctionComparison";
|
||||
|
||||
protected DecompilerCodeComparisonPanel diffPanel;
|
||||
protected DecompilerCodeComparisonView comparisonProvider;
|
||||
protected boolean disableOnReadOnly;
|
||||
|
||||
/**
|
||||
|
@ -33,13 +33,13 @@ public abstract class AbstractMatchedTokensAction extends DockingAction {
|
|||
*
|
||||
* @param actionName name of action
|
||||
* @param owner owner of action
|
||||
* @param diffPanel diff panel containing action
|
||||
* @param comparisonProvider diff panel containing action
|
||||
* @param disableOnReadOnly if true, action will be disabled for read-only programs
|
||||
*/
|
||||
public AbstractMatchedTokensAction(String actionName, String owner,
|
||||
DecompilerCodeComparisonPanel diffPanel, boolean disableOnReadOnly) {
|
||||
DecompilerCodeComparisonView comparisonProvider, boolean disableOnReadOnly) {
|
||||
super(actionName, owner);
|
||||
this.diffPanel = diffPanel;
|
||||
this.comparisonProvider = comparisonProvider;
|
||||
this.disableOnReadOnly = disableOnReadOnly;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,14 +33,9 @@ public class ApplyCalleeEmptySignatureFromMatchedTokensAction
|
|||
private PluginTool tool;
|
||||
public static final String ACTION_NAME = "Function Comparison Apply Callee Signature";
|
||||
|
||||
/**
|
||||
* Construtor
|
||||
* @param diffPanel diff panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public ApplyCalleeEmptySignatureFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonPanel diffPanel, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -53,7 +48,7 @@ public class ApplyCalleeEmptySignatureFromMatchedTokensAction
|
|||
@Override
|
||||
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
|
||||
Function activeFunction = activeSide == Side.LEFT ? leftFunction : rightFunction;
|
||||
Function otherFunction = activeSide == Side.LEFT ? rightFunction : leftFunction;
|
||||
|
|
|
@ -33,14 +33,9 @@ public class ApplyCalleeFunctionNameFromMatchedTokensAction
|
|||
private PluginTool tool;
|
||||
public static final String ACTION_NAME = "Function Comparison Apply Callee Name";
|
||||
|
||||
/**
|
||||
* Construtor
|
||||
* @param diffPanel diff panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public ApplyCalleeFunctionNameFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonPanel diffPanel, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -52,7 +47,7 @@ public class ApplyCalleeFunctionNameFromMatchedTokensAction
|
|||
@Override
|
||||
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
|
||||
Function activeFunction = activeSide == Side.LEFT ? leftFunction : rightFunction;
|
||||
Function otherFunction = activeSide == Side.LEFT ? rightFunction : leftFunction;
|
||||
|
|
|
@ -33,14 +33,9 @@ public class ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction
|
|||
public static final String ACTION_NAME =
|
||||
"Function Comparison Apply Callee Signature And Datatypes";
|
||||
|
||||
/**
|
||||
* Construtor
|
||||
* @param diffPanel diff panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonPanel diffPanel, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -53,7 +48,7 @@ public class ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction
|
|||
@Override
|
||||
protected void doCalleeActionPerformed(Function leftFunction, Function rightFunction) {
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
|
||||
Function activeFunction = activeSide == Side.LEFT ? leftFunction : rightFunction;
|
||||
Function otherFunction = activeSide == Side.LEFT ? rightFunction : leftFunction;
|
||||
|
|
|
@ -35,9 +35,9 @@ public class ApplyEmptyVariableTypeFromMatchedTokensAction extends AbstractMatch
|
|||
public static final String ACTION_NAME = "Function Comparison Apply Variable Skeleton Type";
|
||||
private static final String MENU_GROUP = "A1_ApplyVariable";
|
||||
|
||||
public ApplyEmptyVariableTypeFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||
PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
public ApplyEmptyVariableTypeFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -73,7 +73,7 @@ public class ApplyEmptyVariableTypeFromMatchedTokensAction extends AbstractMatch
|
|||
protected void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||
TokenPair currentPair = context.getTokenPair();
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
|
||||
ClangVariableToken activeToken =
|
||||
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
|
||||
|
@ -87,7 +87,7 @@ public class ApplyEmptyVariableTypeFromMatchedTokensAction extends AbstractMatch
|
|||
HighSymbol otherHighSymbol =
|
||||
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
|
||||
|
||||
Function activeFunction = context.getCodeComparisonPanel().getFunction(activeSide);
|
||||
Function activeFunction = context.getCodeComparisonView().getFunction(activeSide);
|
||||
Program activeProgram = activeFunction.getProgram();
|
||||
|
||||
DataType dt = otherHighSymbol.getDataType();
|
||||
|
|
|
@ -36,14 +36,9 @@ public class ApplyGlobalNameFromMatchedTokensAction extends AbstractMatchedToken
|
|||
public static final String ACTION_NAME = "Function Comparison Apply Global Variable Name";
|
||||
private static final String MENU_GROUP = "A1_ApplyVariable";
|
||||
|
||||
/**
|
||||
* Construtor
|
||||
* @param diffPanel diff panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public ApplyGlobalNameFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||
PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
public ApplyGlobalNameFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -83,7 +78,7 @@ public class ApplyGlobalNameFromMatchedTokensAction extends AbstractMatchedToken
|
|||
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||
TokenPair currentPair = context.getTokenPair();
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
ClangVariableToken activeToken =
|
||||
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
|
||||
: (ClangVariableToken) currentPair.rightToken();
|
||||
|
@ -96,7 +91,7 @@ public class ApplyGlobalNameFromMatchedTokensAction extends AbstractMatchedToken
|
|||
HighSymbol otherHighSymbol =
|
||||
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
|
||||
|
||||
Program activeProgram = context.getCodeComparisonPanel().getProgram(activeSide);
|
||||
Program activeProgram = context.getCodeComparisonView().getProgram(activeSide);
|
||||
|
||||
Symbol activeSymbol = null;
|
||||
if (activeHighSymbol instanceof HighCodeSymbol activeCodeSymbol) {
|
||||
|
|
|
@ -36,14 +36,9 @@ public class ApplyLocalNameFromMatchedTokensAction extends AbstractMatchedTokens
|
|||
public static final String ACTION_NAME = "Function Comparison Apply Local Variable Name";
|
||||
private static final String MENU_GROUP = "A1_ApplyVariable";
|
||||
|
||||
/**
|
||||
* Construtor
|
||||
* @param diffPanel diff panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public ApplyLocalNameFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||
PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
public ApplyLocalNameFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -82,7 +77,7 @@ public class ApplyLocalNameFromMatchedTokensAction extends AbstractMatchedTokens
|
|||
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||
TokenPair currentPair = context.getTokenPair();
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
|
||||
ClangVariableToken activeToken =
|
||||
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
|
||||
|
@ -96,7 +91,7 @@ public class ApplyLocalNameFromMatchedTokensAction extends AbstractMatchedTokens
|
|||
HighSymbol otherHighSymbol =
|
||||
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
|
||||
|
||||
Function activeFunction = context.getCodeComparisonPanel().getFunction(activeSide);
|
||||
Function activeFunction = context.getCodeComparisonView().getFunction(activeSide);
|
||||
Program activeProgram = activeFunction.getProgram();
|
||||
|
||||
try {
|
||||
|
|
|
@ -37,14 +37,9 @@ public class ApplyVariableTypeFromMatchedTokensAction extends AbstractMatchedTok
|
|||
public static final String ACTION_NAME = "Function Comparison Apply Variable Type";
|
||||
private static final String MENU_GROUP = "A1_ApplyVariable";
|
||||
|
||||
/**
|
||||
* Construtor
|
||||
* @param diffPanel diff panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public ApplyVariableTypeFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||
PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, true);
|
||||
public ApplyVariableTypeFromMatchedTokensAction(
|
||||
DecompilerCodeComparisonView comparisonProvider, PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), comparisonProvider, true);
|
||||
this.tool = tool;
|
||||
|
||||
MenuData menuData =
|
||||
|
@ -80,7 +75,7 @@ public class ApplyVariableTypeFromMatchedTokensAction extends AbstractMatchedTok
|
|||
public void dualDecompilerActionPerformed(DualDecompilerActionContext context) {
|
||||
TokenPair currentPair = context.getTokenPair();
|
||||
|
||||
Side activeSide = diffPanel.getActiveSide();
|
||||
Side activeSide = comparisonProvider.getActiveSide();
|
||||
|
||||
ClangVariableToken activeToken =
|
||||
activeSide == Side.LEFT ? (ClangVariableToken) currentPair.leftToken()
|
||||
|
@ -94,7 +89,7 @@ public class ApplyVariableTypeFromMatchedTokensAction extends AbstractMatchedTok
|
|||
HighSymbol otherHighSymbol =
|
||||
otherToken.getHighSymbol(context.getHighFunction(activeSide.otherSide()));
|
||||
|
||||
Function activeFunction = context.getCodeComparisonPanel().getFunction(activeSide);
|
||||
Function activeFunction = context.getCodeComparisonView().getFunction(activeSide);
|
||||
Program activeProgram = activeFunction.getProgram();
|
||||
|
||||
DataType dt = otherHighSymbol.getDataType();
|
||||
|
|
|
@ -39,7 +39,6 @@ import ghidra.program.util.ProgramLocation;
|
|||
public class CDisplay {
|
||||
private final static String OPTIONS_TITLE = "Decompiler";
|
||||
|
||||
private ServiceProvider serviceProvider;
|
||||
private DecompilerController controller;
|
||||
private DecompileOptions decompileOptions;
|
||||
private FieldLocation lastCursorPosition;
|
||||
|
@ -53,7 +52,6 @@ public class CDisplay {
|
|||
DecompileResultsListener decompileListener,
|
||||
Consumer<ProgramLocation> locationConsumer) {
|
||||
|
||||
this.serviceProvider = serviceProvider;
|
||||
highlightController = new DiffClangHighlightController(comparisonOptions);
|
||||
|
||||
decompileOptions = new DecompileOptions();
|
||||
|
@ -138,10 +136,6 @@ public class CDisplay {
|
|||
controller.dispose();
|
||||
}
|
||||
|
||||
public DecompilerController getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
saveCursorPosition();
|
||||
DecompileData data = getDecompileData();
|
||||
|
@ -176,8 +170,8 @@ public class CDisplay {
|
|||
}
|
||||
ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
||||
ToolOptions options = tool.getOptions(OPTIONS_TITLE);
|
||||
Program program = function == null ? null : function.getProgram();
|
||||
decompileOptions.grabFromToolAndProgram(fieldOptions, options, program);
|
||||
Program p = function == null ? null : function.getProgram();
|
||||
decompileOptions.grabFromToolAndProgram(fieldOptions, options, p);
|
||||
}
|
||||
|
||||
DiffClangHighlightController getHighlightController() {
|
||||
|
|
|
@ -35,7 +35,7 @@ public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedCalleeTo
|
|||
* @param diffPanel diff Panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public CompareFuncsFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||
public CompareFuncsFromMatchedTokensAction(DecompilerCodeComparisonView diffPanel,
|
||||
PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, false);
|
||||
this.tool = tool;
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -151,4 +151,9 @@ public class DecompilerCodeComparisonOptions implements OptionsChangeListener {
|
|||
optionsChangedCallback.call();
|
||||
}
|
||||
|
||||
public void dispose(PluginTool tool) {
|
||||
ToolOptions options =
|
||||
tool.getOptions(DecompilerCodeComparisonOptions.OPTIONS_CATEGORY_NAME);
|
||||
options.removeOptionsChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import docking.options.OptionsService;
|
|||
import generic.theme.GIcon;
|
||||
import ghidra.app.decompiler.component.DecompileData;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonPanel;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonView;
|
||||
import ghidra.features.codecompare.graphanalysis.TokenBin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
@ -49,10 +49,9 @@ import resources.Icons;
|
|||
import resources.MultiIcon;
|
||||
|
||||
/**
|
||||
* Panel that displays two decompilers for comparison
|
||||
* UI that displays two decompilers for comparison
|
||||
*/
|
||||
public class DecompilerCodeComparisonPanel
|
||||
extends CodeComparisonPanel {
|
||||
public class DecompilerCodeComparisonView extends CodeComparisonView {
|
||||
|
||||
public static final String NAME = "Decompiler View";
|
||||
|
||||
|
@ -61,7 +60,7 @@ public class DecompilerCodeComparisonPanel
|
|||
private Duo<CDisplay> cDisplays = new Duo<>();
|
||||
|
||||
private DecompilerCodeComparisonOptions comparisonOptions;
|
||||
private CodeDiffFieldPanelCoordinator coordinator;
|
||||
private DualDecompilerScrollCoordinator coordinator;
|
||||
private DecompileDataDiff decompileDataDiff;
|
||||
|
||||
private ToggleExactConstantMatching toggleExactConstantMatchingAction;
|
||||
|
@ -74,7 +73,7 @@ public class DecompilerCodeComparisonPanel
|
|||
* @param owner the owner of this panel
|
||||
* @param tool the tool displaying this panel
|
||||
*/
|
||||
public DecompilerCodeComparisonPanel(String owner, PluginTool tool) {
|
||||
public DecompilerCodeComparisonView(String owner, PluginTool tool) {
|
||||
super(owner, tool);
|
||||
comparisonOptions = new DecompilerCodeComparisonOptions(tool, () -> repaint());
|
||||
|
||||
|
@ -119,7 +118,7 @@ public class DecompilerCodeComparisonPanel
|
|||
public void dispose() {
|
||||
setSynchronizedScrolling(false); // disposes any exiting coordinator
|
||||
cDisplays.each(CDisplay::dispose);
|
||||
comparisonOptions = null;
|
||||
comparisonOptions.dispose(tool);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,6 +188,7 @@ public class DecompilerCodeComparisonPanel
|
|||
actions.add(new ApplyCalleeFunctionNameFromMatchedTokensAction(this, tool));
|
||||
actions.add(new ApplyCalleeEmptySignatureFromMatchedTokensAction(this, tool));
|
||||
actions.add(new ApplyCalleeSignatureWithDatatypesFromMatchedTokensAction(this, tool));
|
||||
|
||||
}
|
||||
|
||||
private void decompileDataSet(Side side, DecompileData dcompileData) {
|
||||
|
@ -272,8 +272,9 @@ public class DecompilerCodeComparisonPanel
|
|||
}
|
||||
}
|
||||
|
||||
private CodeDiffFieldPanelCoordinator createCoordinator() {
|
||||
CodeDiffFieldPanelCoordinator panelCoordinator = new CodeDiffFieldPanelCoordinator(this);
|
||||
private DualDecompilerScrollCoordinator createCoordinator() {
|
||||
DualDecompilerScrollCoordinator panelCoordinator =
|
||||
new DualDecompilerScrollCoordinator(this);
|
||||
if (decompileDataDiff != null) {
|
||||
TaskBuilder.withRunnable(monitor -> {
|
||||
try {
|
||||
|
@ -371,7 +372,7 @@ public class DecompilerCodeComparisonPanel
|
|||
this.setToolBarData(new ToolBarData(NO_EXACT_CONSTANT_MATCHING_ICON, "toggles"));
|
||||
|
||||
setDescription(HTMLUtilities.toHTML("Toggle whether or not constants must\n" +
|
||||
"be exactly the same value to be a match\n" + "in the Decomiler Diff View."));
|
||||
"be exactly the same value to be a match\nin the Decomiler Diff View."));
|
||||
setSelected(false);
|
||||
setEnabled(true);
|
||||
}
|
|
@ -67,11 +67,11 @@ public class DecompilerDiffViewFindAction extends DockingAction {
|
|||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
DualDecompilerActionContext dualContext = (DualDecompilerActionContext) context;
|
||||
DecompilerCodeComparisonPanel decompilerCompPanel =
|
||||
dualContext.getCodeComparisonPanel();
|
||||
DecompilerCodeComparisonView provider =
|
||||
dualContext.getCodeComparisonView();
|
||||
|
||||
Side focusedSide = decompilerCompPanel.getActiveSide();
|
||||
DecompilerPanel focusedPanel = decompilerCompPanel.getDecompilerPanel(focusedSide);
|
||||
Side focusedSide = provider.getActiveSide();
|
||||
DecompilerPanel focusedPanel = provider.getDecompilerPanel(focusedSide);
|
||||
FindDialog dialog = findDialogs.get(focusedSide);
|
||||
if (dialog == null) {
|
||||
dialog = createFindDialog(focusedPanel, focusedSide);
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -33,12 +33,12 @@ public class DetermineDecompilerDifferencesTask extends Task {
|
|||
|
||||
private DecompileDataDiff decompileDataDiff;
|
||||
|
||||
private CodeDiffFieldPanelCoordinator decompilerFieldPanelCoordinator;
|
||||
private DualDecompilerScrollCoordinator decompilerFieldPanelCoordinator;
|
||||
|
||||
public DetermineDecompilerDifferencesTask(DecompileDataDiff decompileDataDiff,
|
||||
boolean matchConstantsExactly, DiffClangHighlightController leftHighlightController,
|
||||
DiffClangHighlightController rightHighlightController,
|
||||
CodeDiffFieldPanelCoordinator decompilerFieldPanelCoordinator) {
|
||||
DualDecompilerScrollCoordinator decompilerFieldPanelCoordinator) {
|
||||
|
||||
super("Mapping C Tokens Between Functions", true, true, true);
|
||||
this.decompileDataDiff = decompileDataDiff;
|
||||
|
|
|
@ -18,7 +18,6 @@ package ghidra.features.codecompare.decompile;
|
|||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
|
@ -38,26 +37,26 @@ import ghidra.util.datastruct.Duo.Side;
|
|||
public class DualDecompilerActionContext extends CodeComparisonActionContext
|
||||
implements RestrictedAddressSetContext {
|
||||
|
||||
private DecompilerCodeComparisonPanel decompilerComparisonPanel = null;
|
||||
private DecompilerCodeComparisonView comparisonProvider = null;
|
||||
private TokenPair tokenPair;
|
||||
private boolean overrideReadOnly = false;
|
||||
|
||||
/**
|
||||
* Creates an action context for a dual decompiler panel.
|
||||
* @param provider the provider for this context
|
||||
* @param panel the DecompilerComparisonPanel
|
||||
* @param comparisonProvider the DecompilerComparisonPanel
|
||||
* @param source the source of the action
|
||||
*/
|
||||
public DualDecompilerActionContext(ComponentProvider provider,
|
||||
DecompilerCodeComparisonPanel panel, Component source) {
|
||||
super(provider, panel, source);
|
||||
decompilerComparisonPanel = panel;
|
||||
DecompilerCodeComparisonView comparisonProvider, Component source) {
|
||||
super(provider, comparisonProvider, source);
|
||||
this.comparisonProvider = comparisonProvider;
|
||||
tokenPair = computeTokenPair();
|
||||
}
|
||||
|
||||
private TokenPair computeTokenPair() {
|
||||
DecompilerPanel focusedPanel =
|
||||
decompilerComparisonPanel.getActiveDisplay().getDecompilerPanel();
|
||||
comparisonProvider.getActiveDisplay().getDecompilerPanel();
|
||||
|
||||
if (!(focusedPanel.getCurrentLocation() instanceof DecompilerLocation focusedLocation)) {
|
||||
return null;
|
||||
|
@ -67,7 +66,7 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
|||
if (focusedToken == null) {
|
||||
return null;
|
||||
}
|
||||
List<TokenBin> tokenBin = decompilerComparisonPanel.getHighBins();
|
||||
List<TokenBin> tokenBin = comparisonProvider.getHighBins();
|
||||
if (tokenBin == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -80,13 +79,9 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
|||
return null;
|
||||
}
|
||||
|
||||
//loop over the tokens in the matching bin and return the first one in the same
|
||||
//class as focusedToken
|
||||
Iterator<ClangToken> tokenIter = matchedBin.iterator();
|
||||
while (tokenIter.hasNext()) {
|
||||
ClangToken currentMatch = tokenIter.next();
|
||||
for (ClangToken currentMatch : matchedBin) {
|
||||
if (currentMatch.getClass().equals(focusedToken.getClass())) {
|
||||
return decompilerComparisonPanel.getActiveSide() == LEFT
|
||||
return comparisonProvider.getActiveSide() == LEFT
|
||||
? new TokenPair(focusedToken, currentMatch)
|
||||
: new TokenPair(currentMatch, focusedToken);
|
||||
}
|
||||
|
@ -95,12 +90,12 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link DecompilerCodeComparisonPanel} that generated this context
|
||||
* Returns the {@link DecompilerCodeComparisonView} that generated this context
|
||||
* @return the decompiler comparison panel that generated this context
|
||||
*/
|
||||
@Override
|
||||
public DecompilerCodeComparisonPanel getCodeComparisonPanel() {
|
||||
return decompilerComparisonPanel;
|
||||
public DecompilerCodeComparisonView getCodeComparisonView() {
|
||||
return comparisonProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,7 +106,7 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
|||
* context
|
||||
*/
|
||||
public HighFunction getHighFunction(Side side) {
|
||||
return decompilerComparisonPanel.getDecompilerPanel(side).getController().getHighFunction();
|
||||
return comparisonProvider.getDecompilerPanel(side).getController().getHighFunction();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,7 +139,7 @@ public class DualDecompilerActionContext extends CodeComparisonActionContext
|
|||
}
|
||||
|
||||
Program activeProgram =
|
||||
decompilerComparisonPanel.getProgram(decompilerComparisonPanel.getActiveSide());
|
||||
comparisonProvider.getProgram(comparisonProvider.getActiveSide());
|
||||
|
||||
if (activeProgram == null) {
|
||||
return true;
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.codecompare.decompile;
|
||||
|
||||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelCoordinator;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
abstract public class DualDecompilerFieldPanelCoordinator extends LineLockedFieldPanelCoordinator {
|
||||
|
||||
public DualDecompilerFieldPanelCoordinator(
|
||||
DecompilerCodeComparisonPanel dualDecompilerPanel) {
|
||||
super(new FieldPanel[] { dualDecompilerPanel.getDecompilerPanel(LEFT).getFieldPanel(),
|
||||
dualDecompilerPanel.getDecompilerPanel(RIGHT).getFieldPanel() });
|
||||
}
|
||||
|
||||
abstract public void leftLocationChanged(ProgramLocation leftLocation);
|
||||
|
||||
abstract public void rightLocationChanged(ProgramLocation rightLocation);
|
||||
}
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -24,6 +24,8 @@ import java.util.List;
|
|||
import org.apache.commons.collections4.BidiMap;
|
||||
import org.apache.commons.collections4.bidimap.DualHashBidiMap;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelScrollCoordinator;
|
||||
import docking.widgets.fieldpanel.support.ViewerPosition;
|
||||
import ghidra.app.decompiler.*;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
|
@ -33,11 +35,7 @@ import ghidra.program.util.ProgramLocation;
|
|||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Class to coordinate the scrolling of two decompiler panels as well as cursor location
|
||||
* highlighting due to cursor location changes.
|
||||
*/
|
||||
public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoordinator {
|
||||
public class DualDecompilerScrollCoordinator extends LineLockedFieldPanelScrollCoordinator {
|
||||
|
||||
private BidiMap<Integer, Integer> leftToRightLineNumberPairing;
|
||||
private List<ClangLine> leftLines = new ArrayList<ClangLine>();
|
||||
|
@ -51,12 +49,13 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
|
|||
|
||||
/**
|
||||
* Constructor
|
||||
* @param dualDecompilerPanel decomp comparison panel
|
||||
* @param comparisonProvider decomp comparison provider
|
||||
*/
|
||||
public CodeDiffFieldPanelCoordinator(DecompilerCodeComparisonPanel dualDecompilerPanel) {
|
||||
super(dualDecompilerPanel);
|
||||
this.leftDecompilerPanel = dualDecompilerPanel.getDecompilerPanel(LEFT);
|
||||
this.rightDecompilerPanel = dualDecompilerPanel.getDecompilerPanel(RIGHT);
|
||||
public DualDecompilerScrollCoordinator(DecompilerCodeComparisonView comparisonProvider) {
|
||||
super(new FieldPanel[] { comparisonProvider.getDecompilerPanel(LEFT).getFieldPanel(),
|
||||
comparisonProvider.getDecompilerPanel(RIGHT).getFieldPanel() });
|
||||
this.leftDecompilerPanel = comparisonProvider.getDecompilerPanel(LEFT);
|
||||
this.rightDecompilerPanel = comparisonProvider.getDecompilerPanel(RIGHT);
|
||||
leftToRightLineNumberPairing = new DualHashBidiMap<>();
|
||||
}
|
||||
|
||||
|
@ -89,7 +88,6 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leftLocationChanged(ProgramLocation leftLocation) {
|
||||
DecompilerLocation leftDecompilerLocation = (DecompilerLocation) leftLocation;
|
||||
|
||||
|
@ -107,7 +105,6 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
|
|||
panelViewChanged(leftDecompilerPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rightLocationChanged(ProgramLocation rightLocation) {
|
||||
DecompilerLocation rightDecompilerLocation = (DecompilerLocation) rightLocation;
|
||||
|
||||
|
@ -301,5 +298,4 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
|
|||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.codecompare.functiongraph;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonActionContext;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonView;
|
||||
|
||||
/**
|
||||
* Action context for a dual Function Graph panel.
|
||||
*/
|
||||
public class FgComparisonContext extends CodeComparisonActionContext {
|
||||
|
||||
private FunctionGraphCodeComparisonView fgProvider;
|
||||
private FgDisplay display;
|
||||
private boolean isLeft;
|
||||
|
||||
public FgComparisonContext(ComponentProvider provider,
|
||||
FunctionGraphCodeComparisonView fgPanel,
|
||||
FgDisplay display, Component component, boolean isLeft) {
|
||||
super(provider, fgPanel, component);
|
||||
this.fgProvider = fgPanel;
|
||||
this.display = display;
|
||||
this.isLeft = isLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeComparisonView getCodeComparisonView() {
|
||||
return fgProvider;
|
||||
}
|
||||
|
||||
public FgDisplay getDisplay() {
|
||||
return display;
|
||||
}
|
||||
|
||||
public boolean isLeft() {
|
||||
return isLeft;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,535 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.codecompare.functiongraph;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.action.DockingAction;
|
||||
import docking.tool.ToolConstants;
|
||||
import ghidra.app.nav.*;
|
||||
import ghidra.app.plugin.core.colorizer.ColorizingService;
|
||||
import ghidra.app.plugin.core.functiongraph.*;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutOptions;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.flowchart.FlowChartLayoutProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.*;
|
||||
import ghidra.app.services.ClipboardService;
|
||||
import ghidra.app.util.ListingHighlightProvider;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.framework.model.DomainObjectChangedEvent;
|
||||
import ghidra.framework.model.DomainObjectListener;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.ServiceListener;
|
||||
import ghidra.graph.viewer.options.VisualGraphOptions;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.UniversalIdGenerator;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
|
||||
/**
|
||||
* This class displays a Function Graph in the left or right side of the Function Comparison view.
|
||||
*/
|
||||
public class FgDisplay implements OptionsChangeListener {
|
||||
|
||||
private static final String FUNCTION_GRAPH_NAME = "Function Graph";
|
||||
|
||||
private PluginTool tool;
|
||||
private String owner;
|
||||
private Program program;
|
||||
private FGController controller;
|
||||
private FgOptions fgOptions;
|
||||
private FormatManager userDefinedFormatManager;
|
||||
private ProgramLocation currentLocation;
|
||||
|
||||
private FgDisplayProgramListener programListener = new FgDisplayProgramListener();
|
||||
private FgServiceListener serviceListener = new FgServiceListener();
|
||||
|
||||
private FGColorProvider colorProvider;
|
||||
private List<FGLayoutProvider> layoutProviders;
|
||||
|
||||
private Consumer<ProgramLocation> locationConsumer;
|
||||
private Consumer<FgDisplay> graphChangedConsumer;
|
||||
|
||||
// Note: this class should probably be using code block highlights and not the code-level
|
||||
// highlights already provided by the Listing.
|
||||
// FgHighlighter highlighter;
|
||||
|
||||
FgDisplay(FunctionGraphCodeComparisonView fgView,
|
||||
Consumer<ProgramLocation> locationConsumer, Consumer<FgDisplay> graphChangedConsumer) {
|
||||
|
||||
this.tool = fgView.getTool();
|
||||
this.owner = fgView.getOwner();
|
||||
this.locationConsumer = locationConsumer;
|
||||
this.graphChangedConsumer = graphChangedConsumer;
|
||||
fgOptions = new FgOptions();
|
||||
|
||||
layoutProviders = loadLayoutProviders();
|
||||
colorProvider = new IndependentColorProvider(tool);
|
||||
|
||||
init();
|
||||
|
||||
FgComparisonEnv env = new FgComparisonEnv();
|
||||
FGControllerListener listener = new FgCoparisonControllerListener();
|
||||
controller = new FGController(env, listener);
|
||||
|
||||
setDefaultLayout();
|
||||
}
|
||||
|
||||
private void setDefaultLayout() {
|
||||
FGLayoutProvider initialLayout = layoutProviders.get(0);
|
||||
for (FGLayoutProvider layout : layoutProviders) {
|
||||
if (layout.getClass().equals(FlowChartLayoutProvider.class)) {
|
||||
initialLayout = layout;
|
||||
break;
|
||||
}
|
||||
}
|
||||
controller.changeLayout(initialLayout);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
|
||||
tool.addServiceListener(serviceListener);
|
||||
|
||||
ColorizingService colorizingService = tool.getService(ColorizingService.class);
|
||||
if (colorizingService != null) {
|
||||
colorProvider = new ToolBasedColorProvider(() -> program, colorizingService);
|
||||
}
|
||||
}
|
||||
|
||||
private List<FGLayoutProvider> loadLayoutProviders() {
|
||||
|
||||
// Shared Code Note: This code is mirrored in the FgDisplay for the Code Comparison API
|
||||
|
||||
FGLayoutFinder layoutFinder = new DiscoverableFGLayoutFinder();
|
||||
List<FGLayoutProvider> instances = layoutFinder.findLayouts();
|
||||
if (instances.isEmpty()) {
|
||||
throw new AssertException("Could not find any layout providers. You project may not " +
|
||||
"be configured properly.");
|
||||
}
|
||||
|
||||
List<FGLayoutProvider> layouts = new ArrayList<>(instances);
|
||||
Collections.sort(layouts, (o1, o2) -> -o1.getPriorityLevel() + o2.getPriorityLevel());
|
||||
return layouts;
|
||||
}
|
||||
|
||||
private void initializeOptions() {
|
||||
ToolOptions options = tool.getOptions(ToolConstants.GRAPH_OPTIONS);
|
||||
options.removeOptionsChangeListener(this);
|
||||
options.addOptionsChangeListener(this);
|
||||
|
||||
// Graph -> Function Graph
|
||||
Options subOptions = options.getOptions(FUNCTION_GRAPH_NAME);
|
||||
|
||||
fgOptions.registerOptions(subOptions);
|
||||
fgOptions.loadOptions(subOptions);
|
||||
|
||||
for (FGLayoutProvider layoutProvider : layoutProviders) {
|
||||
|
||||
// Graph -> Function Graph -> Layout Name
|
||||
String layoutName = layoutProvider.getLayoutName();
|
||||
Options layoutToolOptions = subOptions.getOptions(layoutName);
|
||||
FGLayoutOptions layoutOptions = layoutProvider.createLayoutOptions(layoutToolOptions);
|
||||
if (layoutOptions == null) {
|
||||
continue; // many layouts do not have options
|
||||
}
|
||||
|
||||
layoutOptions.registerOptions(layoutToolOptions);
|
||||
layoutOptions.loadOptions(layoutToolOptions);
|
||||
fgOptions.setLayoutOptions(layoutName, layoutOptions);
|
||||
}
|
||||
}
|
||||
|
||||
public FGController getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public JComponent getComponent() {
|
||||
return controller.getViewComponent();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if (program != null) {
|
||||
program.removeListener(programListener);
|
||||
program = null;
|
||||
}
|
||||
programListener.dispose();
|
||||
controller.cleanup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
|
||||
// Graph -> Function Graph
|
||||
Options subOptions = options.getOptions(FUNCTION_GRAPH_NAME);
|
||||
fgOptions.loadOptions(subOptions);
|
||||
|
||||
controller.optionsChanged();
|
||||
|
||||
if (fgOptions.optionChangeRequiresRelayout(optionName)) {
|
||||
controller.refresh(true);
|
||||
}
|
||||
else if (VisualGraphOptions.VIEW_RESTORE_OPTIONS_KEY.equals(optionName)) {
|
||||
controller.clearViewSettings();
|
||||
}
|
||||
else {
|
||||
controller.refreshDisplayWithoutRebuilding();
|
||||
}
|
||||
}
|
||||
|
||||
public void showFunction(Function function) {
|
||||
updateProgram(function);
|
||||
|
||||
if (function == null) {
|
||||
controller.setStatusMessage("No Function");
|
||||
return;
|
||||
}
|
||||
if (function.isExternal()) {
|
||||
String name = function.getName(true);
|
||||
controller.setStatusMessage("\"" + name + "\" is an external function.");
|
||||
return;
|
||||
}
|
||||
|
||||
Address entry = function.getEntryPoint();
|
||||
currentLocation = new ProgramLocation(program, entry);
|
||||
controller.display(program, currentLocation);
|
||||
}
|
||||
|
||||
public void setLocation(ProgramLocation location) {
|
||||
controller.setLocation(location);
|
||||
}
|
||||
|
||||
public ProgramLocation getLocation() {
|
||||
return controller.getLocation();
|
||||
}
|
||||
|
||||
private void updateProgram(Function function) {
|
||||
Program newProgram = function == null ? null : function.getProgram();
|
||||
if (program == newProgram) {
|
||||
return;
|
||||
}
|
||||
if (program != null) {
|
||||
program.removeListener(programListener);
|
||||
}
|
||||
|
||||
program = newProgram;
|
||||
|
||||
if (program != null) {
|
||||
program.addListener(programListener);
|
||||
initializeOptions();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isBusy() {
|
||||
return controller.isBusy();
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
controller.refresh(true);
|
||||
}
|
||||
|
||||
private class FgComparisonEnv implements FgEnv {
|
||||
|
||||
private Navigatable navigatable = new DummyNavigatable();
|
||||
|
||||
@Override
|
||||
public PluginTool getTool() {
|
||||
return tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionGraphOptions getOptions() {
|
||||
return fgOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FGLayoutProvider> getLayoutProviders() {
|
||||
return layoutProviders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLocalAction(DockingAction action) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public FGColorProvider getColorProvider() {
|
||||
return colorProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FormatManager getUserDefinedFormat() {
|
||||
return userDefinedFormatManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserDefinedFormat(FormatManager format) {
|
||||
userDefinedFormatManager = format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Navigatable getNavigatable() {
|
||||
return navigatable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getToolLocation() {
|
||||
// this isn't really the tool's location, but maybe this is fine for this display
|
||||
return currentLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getGraphLocation() {
|
||||
return currentLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelection(ProgramSelection selection) {
|
||||
controller.setSelection(selection);
|
||||
}
|
||||
}
|
||||
|
||||
private class FgCoparisonControllerListener implements FGControllerListener {
|
||||
|
||||
@Override
|
||||
public void dataChanged() {
|
||||
graphChangedConsumer.accept(FgDisplay.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userChangedLocation(ProgramLocation location, boolean vertexChanged) {
|
||||
currentLocation = location;
|
||||
locationConsumer.accept(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userChangedSelection(ProgramSelection selection) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userSelectedText(String s) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userNavigated(ProgramLocation location) {
|
||||
// stub
|
||||
}
|
||||
}
|
||||
|
||||
private class DummyNavigatable implements Navigatable {
|
||||
|
||||
private long id;
|
||||
|
||||
DummyNavigatable() {
|
||||
id = UniversalIdGenerator.nextID().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getInstanceID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goTo(Program p, ProgramLocation pl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationMemento getMemento() {
|
||||
return new FgMemento(); // dummy
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMemento(LocationMemento memento) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getNavigatableIcon() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMarkers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFocus() {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelection(ProgramSelection selection) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHighlight(ProgramSelection highlight) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramSelection getSelection() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramSelection getHighlight() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTextSelection() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNavigatableListener(NavigatableRemovalListener listener) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNavigatableListener(NavigatableRemovalListener listener) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisposed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsHighlight() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHighlightProvider(ListingHighlightProvider highlightProvider,
|
||||
Program program) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHighlightProvider(ListingHighlightProvider highlightProvider,
|
||||
Program p) {
|
||||
// stub
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class FgMemento extends LocationMemento {
|
||||
FgMemento() {
|
||||
super((Program) null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private class FgDisplayProgramListener implements DomainObjectListener {
|
||||
|
||||
private SwingUpdateManager updater = new SwingUpdateManager(500, 5000, () -> refresh());
|
||||
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||
updater.update();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
updater.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private class FgServiceListener implements ServiceListener {
|
||||
|
||||
@Override
|
||||
public void serviceAdded(Class<?> interfaceClass, Object service) {
|
||||
if (interfaceClass == ClipboardService.class) {
|
||||
// if we decide to support copy/paste in this viewer, then the FGClipboardProvider
|
||||
// needs to be taken out of the FGProvider and made independent. We would also need
|
||||
// to refactor the ClipboardPlugin to not need a provider, instead adding a way to
|
||||
// get a component, context and to add/remove actions.
|
||||
}
|
||||
else if (interfaceClass == ColorizingService.class) {
|
||||
colorProvider =
|
||||
new ToolBasedColorProvider(() -> program, (ColorizingService) service);
|
||||
controller.refresh(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serviceRemoved(Class<?> interfaceClass, Object service) {
|
||||
if (interfaceClass == ColorizingService.class) {
|
||||
colorProvider = new IndependentColorProvider(tool);
|
||||
controller.refresh(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FgOptions extends FunctionGraphOptions {
|
||||
|
||||
@Override
|
||||
public void setUseAnimation(boolean useAnimation) {
|
||||
// don't allow this to be changed; animations seem like overkill for comparisons
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadOptions(Options options) {
|
||||
super.loadOptions(options);
|
||||
|
||||
useAnimation = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.codecompare.functiongraph;
|
||||
|
||||
import ghidra.app.util.viewer.listingpanel.ProgramLocationTranslator;
|
||||
import ghidra.program.util.ListingAddressCorrelation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
|
||||
/**
|
||||
* A class to synchronize locations between the left and right Function Graph comparison panels.
|
||||
*/
|
||||
class FgDisplaySynchronizer {
|
||||
|
||||
private Duo<FgDisplay> displays;
|
||||
private ProgramLocationTranslator locationTranslator;
|
||||
|
||||
FgDisplaySynchronizer(Duo<FgDisplay> displays, ListingAddressCorrelation correlation) {
|
||||
this.displays = displays;
|
||||
this.locationTranslator = new ProgramLocationTranslator(correlation);
|
||||
}
|
||||
|
||||
void setLocation(Side side, ProgramLocation location) {
|
||||
// Only set other side's cursor if we are coordinating right now.
|
||||
Side otherSide = side.otherSide();
|
||||
ProgramLocation otherLocation = locationTranslator.getProgramLocation(otherSide, location);
|
||||
if (otherLocation != null) {
|
||||
displays.get(otherSide).setLocation(otherLocation);
|
||||
}
|
||||
}
|
||||
|
||||
void sync(Side side) {
|
||||
ProgramLocation programLocation = displays.get(side).getLocation();
|
||||
if (programLocation != null) {
|
||||
setLocation(side, programLocation);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
// this object should probably have a dispose() method
|
||||
// locationTranslator.dispose();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,508 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.codecompare.functiongraph;
|
||||
|
||||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.ComponentProvider;
|
||||
import docking.action.DockingAction;
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.*;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.features.base.codecompare.listing.LinearAddressCorrelation;
|
||||
import ghidra.features.base.codecompare.panel.CodeComparisonView;
|
||||
import ghidra.features.codecompare.functiongraph.actions.*;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.graph.viewer.GraphSatelliteListener;
|
||||
import ghidra.program.model.correlate.HashedFunctionAddressCorrelation;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.util.ListingAddressCorrelation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import help.Help;
|
||||
|
||||
/**
|
||||
* Provides a {@link CodeComparisonView} for displaying function graphs.
|
||||
* <P>
|
||||
* Known Issues:
|
||||
* <UL>
|
||||
* <LI>The options used by this API are the same as those for the Function Graph plugin. We
|
||||
* may find that some of the options should not apply to this API. If true, then we would
|
||||
* have to create a new options entry in the tool and a different options object for this
|
||||
* API to use.
|
||||
* </LI>
|
||||
* <LI>Each open panel will potentially update the state that is saved in the tool. This can
|
||||
* lead to confusion when multiple open windows have different settings, as it is not
|
||||
* clear which window's settings will get saved.
|
||||
* </LI>
|
||||
* <LI>The views do not support copying, which is consistent with the other function comparison
|
||||
* views.
|
||||
* </LI>
|
||||
* </UL>
|
||||
*/
|
||||
public class FunctionGraphCodeComparisonView extends CodeComparisonView {
|
||||
|
||||
public static final String NAME = "Function Graph View";
|
||||
|
||||
private static final String FORMAT_KEY = "FIELD_FORMAT";
|
||||
private static final String SHOW_POPUPS_KEY = "SHOW_POPUPS";
|
||||
private static final String SHOW_SATELLITE_KEY = "SHOW_SATELLITE";
|
||||
|
||||
private static final String LAYOUT_NAME = "LAYOUT_NAME";
|
||||
private static final String COMPLEX_LAYOUT_NAME = "COMPLEX_LAYOUT_NAME";
|
||||
private static final String LAYOUT_CLASS_NAME = "LAYOUT_CLASS_NAME";
|
||||
|
||||
private FgDisplaySynchronizer coordinator;
|
||||
|
||||
private Duo<FgDisplay> displays = new Duo<>();
|
||||
private Duo<Function> functions = new Duo<>();
|
||||
|
||||
private ListingAddressCorrelation addressCorrelator;
|
||||
private boolean displaysLocked;
|
||||
|
||||
private SaveState defaultSaveState;
|
||||
private SaveState saveState;
|
||||
private List<DockingAction> actions = new ArrayList<>();
|
||||
private FgTogglePopupsAction showPopupsAction;
|
||||
private FgToggleSatelliteAction showSatelliteAction;
|
||||
|
||||
public FunctionGraphCodeComparisonView(String owner, PluginTool tool) {
|
||||
super(owner, tool);
|
||||
|
||||
Help.getHelpService()
|
||||
.registerHelp(this, new HelpLocation(HELP_TOPIC, "FunctionGraph_Diff_View"));
|
||||
|
||||
displays = buildDisplays();
|
||||
createActions();
|
||||
installSatelliteListeners();
|
||||
buildDefaultSaveState();
|
||||
|
||||
buildPanel();
|
||||
setSynchronizedScrolling(true);
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public Duo<FgDisplay> getDisplays() {
|
||||
return displays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by actions to signal that the user changed something worth saving.
|
||||
*/
|
||||
public void stateChanged() {
|
||||
saveShowPopups(saveState);
|
||||
saveShowSatellite(saveState);
|
||||
saveLayout(saveState);
|
||||
saveCustomFormat(saveState);
|
||||
|
||||
if (!hasStateChanges()) {
|
||||
// This implies the user has made changes, but those changes match the default settings.
|
||||
// Clear the save state so no changes get written to the tool.
|
||||
saveState.clear();
|
||||
}
|
||||
|
||||
tool.setConfigChanged(true);
|
||||
}
|
||||
|
||||
private void buildDefaultSaveState() {
|
||||
SaveState ss = new SaveState();
|
||||
saveShowPopups(ss);
|
||||
saveLayout(ss);
|
||||
saveCustomFormat(ss);
|
||||
defaultSaveState = ss;
|
||||
}
|
||||
|
||||
private void saveShowPopups(SaveState ss) {
|
||||
ss.putBoolean(SHOW_POPUPS_KEY, showPopupsAction.isSelected());
|
||||
}
|
||||
|
||||
private void saveShowSatellite(SaveState ss) {
|
||||
ss.putBoolean(SHOW_SATELLITE_KEY, showSatelliteAction.isSelected());
|
||||
}
|
||||
|
||||
private void loadShowPopups(SaveState ss) {
|
||||
FgDisplay leftDisplay = displays.get(LEFT);
|
||||
FGController leftController = leftDisplay.getController();
|
||||
boolean currentShowPopups = leftController.arePopupsVisible();
|
||||
boolean savedShowPopups = ss.getBoolean(SHOW_POPUPS_KEY, currentShowPopups);
|
||||
if (currentShowPopups == savedShowPopups) {
|
||||
return;
|
||||
}
|
||||
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
|
||||
leftController.setPopupsVisible(savedShowPopups);
|
||||
rightController.setPopupsVisible(savedShowPopups);
|
||||
showPopupsAction.setSelected(savedShowPopups);
|
||||
}
|
||||
|
||||
private void loadShowSatellite(SaveState ss) {
|
||||
FgDisplay leftDisplay = displays.get(LEFT);
|
||||
FGController leftController = leftDisplay.getController();
|
||||
boolean currentShowSatellite = leftController.isSatelliteVisible();
|
||||
boolean savedShowSatellite = ss.getBoolean(SHOW_SATELLITE_KEY, currentShowSatellite);
|
||||
if (currentShowSatellite == savedShowSatellite) {
|
||||
return;
|
||||
}
|
||||
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
|
||||
leftController.setSatelliteVisible(savedShowSatellite);
|
||||
rightController.setSatelliteVisible(savedShowSatellite);
|
||||
showSatelliteAction.setSelected(savedShowSatellite);
|
||||
}
|
||||
|
||||
private void saveLayout(SaveState ss) {
|
||||
FgDisplay display = displays.get(LEFT);
|
||||
FGController controller = display.getController();
|
||||
FGLayoutProvider layout = controller.getLayoutProvider();
|
||||
|
||||
SaveState layoutState = new SaveState(COMPLEX_LAYOUT_NAME);
|
||||
String layoutName = layout.getLayoutName();
|
||||
layoutState.putString(LAYOUT_NAME, layoutName);
|
||||
layoutState.putString(LAYOUT_CLASS_NAME, layout.getClass().getName());
|
||||
ss.putSaveState(COMPLEX_LAYOUT_NAME, layoutState);
|
||||
}
|
||||
|
||||
private void loadLayout(SaveState ss) {
|
||||
|
||||
SaveState layoutState = saveState.getSaveState(COMPLEX_LAYOUT_NAME);
|
||||
if (layoutState == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FgDisplay leftDisplay = displays.get(LEFT);
|
||||
FGController leftController = leftDisplay.getController();
|
||||
FGLayoutProvider layout = leftController.getLayoutProvider();
|
||||
|
||||
String savedLayoutName = layoutState.getString(LAYOUT_NAME, layout.getLayoutName());
|
||||
if (layout.getLayoutName().equals(savedLayoutName)) {
|
||||
return; // already set
|
||||
}
|
||||
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
|
||||
FgEnv env = leftController.getEnv();
|
||||
List<FGLayoutProvider> layoutProviders = new ArrayList<>(env.getLayoutProviders());
|
||||
for (FGLayoutProvider layoutProvider : layoutProviders) {
|
||||
String providerName = layoutProvider.getLayoutName();
|
||||
if (providerName.equals(savedLayoutName)) {
|
||||
leftController.changeLayout(layoutProvider);
|
||||
rightController.changeLayout(layoutProvider);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveCustomFormat(SaveState ss) {
|
||||
FgDisplay display = displays.get(LEFT);
|
||||
FGController controller = display.getController();
|
||||
FormatManager format = controller.getMinimalFormatManager();
|
||||
SaveState formatState = new SaveState();
|
||||
format.saveState(formatState);
|
||||
ss.putSaveState(FORMAT_KEY, formatState);
|
||||
}
|
||||
|
||||
private void loadCustomFormat(SaveState ss) {
|
||||
|
||||
SaveState formatState = ss.getSaveState(FORMAT_KEY);
|
||||
if (formatState == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ToolOptions displayOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_DISPLAY);
|
||||
ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
||||
|
||||
FgDisplay leftDisplay = displays.get(LEFT);
|
||||
FGController leftController = leftDisplay.getController();
|
||||
FormatManager format = leftController.getMinimalFormatManager();
|
||||
SaveState testState = new SaveState();
|
||||
format.saveState(testState);
|
||||
|
||||
if (equals(testState, formatState)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FormatManager formatManager = new FormatManager(displayOptions, fieldOptions);
|
||||
formatManager.readState(formatState);
|
||||
leftController.updateMinimalFormatManager(formatManager);
|
||||
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
rightController.updateMinimalFormatManager(formatManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSaveState(SaveState ss) {
|
||||
this.saveState = ss;
|
||||
|
||||
if (!hasStateChanges()) {
|
||||
return; // the given state matches the default state; nothing to do
|
||||
}
|
||||
|
||||
loadShowPopups(ss);
|
||||
loadShowSatellite(ss);
|
||||
loadLayout(ss);
|
||||
loadCustomFormat(ss);
|
||||
}
|
||||
|
||||
private boolean hasStateChanges() {
|
||||
if (!saveState.isEmpty()) {
|
||||
return !equals(saveState, defaultSaveState);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean equals(SaveState state1, SaveState state2) {
|
||||
String s1 = state1.toString();
|
||||
String s2 = state2.toString();
|
||||
return Objects.equals(s1, s2);
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
|
||||
// Note: many of these actions are similar to what is in the main Function Graph. The way
|
||||
// this class is coded, the actions do not share keybindings. This is something we may wish
|
||||
// to change in the future by making the key binding type sharable.
|
||||
|
||||
// Both displays have the same actions they get from the Function Graph API. We will add
|
||||
// them to the comparison provider. We only need to add one set of actions, since they are
|
||||
// the same for both providers.
|
||||
FgDisplay left = displays.get(LEFT);
|
||||
actions.add(new FgResetGraphAction(left));
|
||||
|
||||
FgDisplay right = displays.get(RIGHT);
|
||||
actions.add(new FgResetGraphAction(right));
|
||||
|
||||
showPopupsAction = new FgTogglePopupsAction(this);
|
||||
FGController controller = left.getController();
|
||||
boolean showPopups = controller.arePopupsVisible();
|
||||
showPopupsAction.setSelected(showPopups);
|
||||
|
||||
showSatelliteAction = new FgToggleSatelliteAction(this);
|
||||
boolean showSatellite = controller.isSatelliteVisible();
|
||||
showSatelliteAction.setSelected(showSatellite);
|
||||
|
||||
actions.add(showSatelliteAction);
|
||||
actions.add(showPopupsAction);
|
||||
actions.add(new FgRelayoutAction(this));
|
||||
actions.add(new FgChooseFormatAction(this));
|
||||
}
|
||||
|
||||
private void installSatelliteListeners() {
|
||||
|
||||
FgDisplay left = displays.get(LEFT);
|
||||
FgDisplay right = displays.get(RIGHT);
|
||||
FGController leftController = left.getController();
|
||||
FGController rightController = right.getController();
|
||||
|
||||
GraphSatelliteListener listener = new GraphSatelliteListener() {
|
||||
|
||||
@Override
|
||||
public void satelliteVisibilityChanged(boolean docked, boolean visible) {
|
||||
if (visible) {
|
||||
leftController.setSatelliteVisible(true);
|
||||
rightController.setSatelliteVisible(true);
|
||||
}
|
||||
showSatelliteAction.setSelected(visible);
|
||||
stateChanged();
|
||||
}
|
||||
};
|
||||
|
||||
FGView lv = leftController.getView();
|
||||
FGView rv = rightController.getView();
|
||||
lv.addSatelliteListener(listener);
|
||||
rv.addSatelliteListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DockingAction> getActions() {
|
||||
List<DockingAction> superActions = super.getActions();
|
||||
superActions.addAll(0, actions);
|
||||
return actions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void comparisonDataChanged() {
|
||||
|
||||
maybeLoadFunction(LEFT, comparisonData.get(LEFT).getFunction());
|
||||
maybeLoadFunction(RIGHT, comparisonData.get(RIGHT).getFunction());
|
||||
|
||||
addressCorrelator = createCorrelator();
|
||||
// updateProgramViews();
|
||||
updateCoordinator();
|
||||
//initializeCursorMarkers();
|
||||
updateActionEnablement();
|
||||
validate();
|
||||
}
|
||||
|
||||
private ListingAddressCorrelation createCorrelator() {
|
||||
Function f1 = getFunction(LEFT);
|
||||
Function f2 = getFunction(RIGHT);
|
||||
if (f1 != null && f2 != null) {
|
||||
try {
|
||||
return new HashedFunctionAddressCorrelation(f1, f2, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (CancelledException | MemoryAccessException e) {
|
||||
// fall back to linear address correlation
|
||||
}
|
||||
}
|
||||
if (comparisonData.get(LEFT).isEmpty() || comparisonData.get(RIGHT).isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new LinearAddressCorrelation(comparisonData);
|
||||
}
|
||||
|
||||
private void updateCoordinator() {
|
||||
if (coordinator != null) {
|
||||
coordinator.dispose();
|
||||
coordinator = null;
|
||||
}
|
||||
if (displaysLocked) {
|
||||
coordinator = new FgDisplaySynchronizer(displays, addressCorrelator);
|
||||
coordinator.sync(activeSide);
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeLoadFunction(Side side, Function function) {
|
||||
// we keep a local copy of the function so we know if it is already decompiled
|
||||
if (functions.get(side) == function) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the scroll info and highlight info to prevent unnecessary highlighting, etc.
|
||||
loadFunction(side, null);
|
||||
loadFunction(side, function);
|
||||
|
||||
}
|
||||
|
||||
private void loadFunction(Side side, Function function) {
|
||||
if (functions.get(side) != function) {
|
||||
functions = functions.with(side, function);
|
||||
displays.get(side).showFunction(function);
|
||||
}
|
||||
}
|
||||
|
||||
private Duo<FgDisplay> buildDisplays() {
|
||||
|
||||
// The function graph's display is not ready until a graph is loaded. It also gets rebuilt
|
||||
// each time the graph is reloaded. To correctly install listeners, we need to update them
|
||||
// as the graph is rebuilt.
|
||||
Consumer<FgDisplay> graphChangedCallback = display -> {
|
||||
Side side = getSide(display);
|
||||
addMouseAndFocusListeners(side);
|
||||
};
|
||||
|
||||
FgDisplay leftDisplay =
|
||||
new FgDisplay(this, l -> locationChanged(LEFT, l), graphChangedCallback);
|
||||
FgDisplay rightDisplay =
|
||||
new FgDisplay(this, l -> locationChanged(RIGHT, l), graphChangedCallback);
|
||||
|
||||
return new Duo<>(leftDisplay, rightDisplay);
|
||||
}
|
||||
|
||||
private Side getSide(FgDisplay display) {
|
||||
FgDisplay leftDisplay = displays.get(LEFT);
|
||||
if (display == leftDisplay) {
|
||||
return LEFT;
|
||||
}
|
||||
return RIGHT;
|
||||
}
|
||||
|
||||
private void locationChanged(Side side, ProgramLocation location) {
|
||||
if (coordinator != null) {
|
||||
coordinator.setLocation(side, location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
setSynchronizedScrolling(false); // disposes any exiting coordinator
|
||||
displays.each(FgDisplay::dispose);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display from the active side.
|
||||
* @return the active display
|
||||
*/
|
||||
public FgDisplay getActiveDisplay() {
|
||||
return displays.get(activeSide);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(ComponentProvider provider, MouseEvent event) {
|
||||
FgDisplay display = getActiveDisplay();
|
||||
Component component = event != null ? event.getComponent()
|
||||
: display.getComponent();
|
||||
boolean isLeft = activeSide == LEFT;
|
||||
return new FgComparisonContext(provider, this, display, component, isLeft);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateActionEnablement() {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSynchronizedScrolling(boolean synchronize) {
|
||||
if (coordinator != null) {
|
||||
coordinator.dispose();
|
||||
coordinator = null;
|
||||
}
|
||||
|
||||
displaysLocked = synchronize;
|
||||
if (displaysLocked) {
|
||||
coordinator = new FgDisplaySynchronizer(displays, addressCorrelator);
|
||||
coordinator.sync(activeSide);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComparisonComponent(Side side) {
|
||||
return displays.get(side).getComponent();
|
||||
}
|
||||
|
||||
public boolean isBusy() {
|
||||
return displays.get(LEFT).isBusy() || displays.get(RIGHT).isBusy();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.codecompare.functiongraph.actions;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import ghidra.features.codecompare.functiongraph.FgComparisonContext;
|
||||
import ghidra.features.codecompare.functiongraph.FgDisplay;
|
||||
|
||||
public abstract class AbstractFgAction extends DockingAction {
|
||||
|
||||
private FgDisplay display;
|
||||
|
||||
protected AbstractFgAction(FgDisplay display, String name) {
|
||||
super(name, display.getOwner());
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
protected boolean isMyDisplay(ActionContext context) {
|
||||
if (!(context instanceof FgComparisonContext fgContext)) {
|
||||
return false;
|
||||
}
|
||||
return fgContext.getDisplay() == display;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return isMyDisplay(context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.codecompare.functiongraph.actions;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.features.codecompare.functiongraph.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
|
||||
public class FgChooseFormatAction extends DockingAction {
|
||||
|
||||
private FunctionGraphCodeComparisonView fgProvider;
|
||||
|
||||
public FgChooseFormatAction(FunctionGraphCodeComparisonView fgProvider) {
|
||||
super("Edit Code Block Fields", fgProvider.getOwner());
|
||||
this.fgProvider = fgProvider;
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Edit Fields" }));
|
||||
|
||||
setHelpLocation(
|
||||
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Format"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
Duo<FgDisplay> displays = fgProvider.getDisplays();
|
||||
FgDisplay leftDisplay = displays.get(Side.LEFT);
|
||||
FGController leftController = leftDisplay.getController();
|
||||
leftController.showFormatChooser();
|
||||
|
||||
FormatManager leftFormatManager = leftController.getMinimalFormatManager();
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
rightController.updateMinimalFormatManager(leftFormatManager);
|
||||
|
||||
fgProvider.stateChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!(context instanceof FgComparisonContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Duo<FgDisplay> displays = fgProvider.getDisplays();
|
||||
FgDisplay leftDisplay = displays.get(Side.LEFT);
|
||||
FGController controller = leftDisplay.getController();
|
||||
return controller.hasResults();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.codecompare.functiongraph.actions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import docking.widgets.dialogs.ObjectChooserDialog;
|
||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FgEnv;
|
||||
import ghidra.features.codecompare.functiongraph.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
|
||||
public class FgRelayoutAction extends DockingAction {
|
||||
|
||||
private FunctionGraphCodeComparisonView fgProvider;
|
||||
|
||||
public FgRelayoutAction(FunctionGraphCodeComparisonView fgProvider) {
|
||||
super("Relayout Graph", fgProvider.getOwner(), KeyBindingType.SHARED);
|
||||
this.fgProvider = fgProvider;
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Relayout Graph" }));
|
||||
|
||||
setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Layout"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
Duo<FgDisplay> displays = fgProvider.getDisplays();
|
||||
FgDisplay leftDisplay = displays.get(Side.LEFT);
|
||||
FGController leftController = leftDisplay.getController();
|
||||
FgEnv env = leftController.getEnv();
|
||||
List<FGLayoutProvider> layoutProviders = new ArrayList<>(env.getLayoutProviders());
|
||||
ObjectChooserDialog<FGLayoutProvider> dialog =
|
||||
new ObjectChooserDialog<>("Choose Layout", FGLayoutProvider.class, layoutProviders,
|
||||
"getLayoutName");
|
||||
FGLayoutProvider currentLayout = leftController.getLayoutProvider();
|
||||
dialog.setSelectedObject(currentLayout);
|
||||
PluginTool tool = env.getTool();
|
||||
tool.showDialog(dialog);
|
||||
|
||||
FGLayoutProvider layoutProvider = dialog.getSelectedObject();
|
||||
if (layoutProvider == null) {
|
||||
return; // cancelled
|
||||
}
|
||||
leftController.changeLayout(layoutProvider);
|
||||
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
rightController.changeLayout(layoutProvider);
|
||||
|
||||
fgProvider.stateChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return context instanceof FgComparisonContext;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.codecompare.functiongraph.actions;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
|
||||
import ghidra.features.codecompare.functiongraph.FgDisplay;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
public class FgResetGraphAction extends AbstractFgAction {
|
||||
|
||||
private FgDisplay display;
|
||||
|
||||
public FgResetGraphAction(FgDisplay display) {
|
||||
super(display, "Reset Graph");
|
||||
this.display = display;
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Reset Graph" }));
|
||||
|
||||
setHelpLocation(
|
||||
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Reload_Graph"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
FGController controller = display.getController();
|
||||
JComponent component = controller.getViewComponent();
|
||||
int choice = OptionDialog.showYesNoDialog(component, "Reset Graph?",
|
||||
"<html>Erase all vertex position and grouping information?");
|
||||
if (choice != OptionDialog.YES_OPTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
controller.resetGraph();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!super.isEnabledForContext(context)) {
|
||||
return false;
|
||||
}
|
||||
FGController controller = display.getController();
|
||||
return controller.hasResults();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.features.codecompare.functiongraph.actions;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.MenuData;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
|
||||
import ghidra.features.codecompare.functiongraph.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
|
||||
/**
|
||||
* An action to toggle popup enablement for the Function Graph comparison views.
|
||||
*/
|
||||
public class FgTogglePopupsAction extends ToggleDockingAction {
|
||||
|
||||
private FunctionGraphCodeComparisonView fgProvider;
|
||||
|
||||
public FgTogglePopupsAction(FunctionGraphCodeComparisonView fgProvider) {
|
||||
super("Display Popup Windows", fgProvider.getOwner());
|
||||
this.fgProvider = fgProvider;
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Display Popup Windows" }));
|
||||
|
||||
setHelpLocation(new HelpLocation("FunctionGraphPlugin", "Popups"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
Duo<FgDisplay> displays = fgProvider.getDisplays();
|
||||
FgDisplay leftDisplay = displays.get(Side.LEFT);
|
||||
|
||||
FGController controller = leftDisplay.getController();
|
||||
boolean visible = isSelected();
|
||||
controller.setPopupsVisible(visible);
|
||||
|
||||
FgDisplay rightDisplay = displays.get(Side.RIGHT);
|
||||
FGController rightController = rightDisplay.getController();
|
||||
rightController.setPopupsVisible(visible);
|
||||
|
||||
fgProvider.stateChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!(context instanceof FgComparisonContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Duo<FgDisplay> displays = fgProvider.getDisplays();
|
||||
FgDisplay leftDisplay = displays.get(Side.LEFT);
|
||||
FGController controller = leftDisplay.getController();
|
||||
return controller.hasResults();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue