-
-
Notifications
You must be signed in to change notification settings - Fork 92
/
Copy pathEvinceConversation.kt
107 lines (94 loc) · 4.77 KB
/
EvinceConversation.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package nl.hannahsten.texifyidea.run.linuxpdfviewer.evince
import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.openapi.project.Project
import nl.hannahsten.texifyidea.TeXception
import nl.hannahsten.texifyidea.run.linuxpdfviewer.ViewerConversation
import nl.hannahsten.texifyidea.util.runCommand
import org.freedesktop.dbus.connections.impl.DBusConnection
import org.freedesktop.dbus.errors.NoReply
import org.freedesktop.dbus.errors.ServiceUnknown
import org.gnome.evince.Daemon
/**
* Send commands to Evince.
* For more information about D-Bus and forward/inverse search, see https://github.com/PHPirates/evince_dbus
*
* @author Thomas Schouten
*/
object EvinceConversation : ViewerConversation() {
/**
* Object path of the Evince daemon. Together with the object name, this allows us to find the
* D-Bus object which allows us to execute the FindDocument function, which is exported on the D-Bus
* by Evince.
*/
private const val evinceDaemonPath = "/org/gnome/evince/Daemon"
/**
* Object name of the Evince daemon.
*/
private const val evinceDaemonName = "org.gnome.evince.Daemon"
/**
* This variable will hold the latest known Evince process owner. We need to know the owner of the pdf file in order to execute forward search.
*/
private var processOwner: String? = null
/**
* Open a file in Evince, starting it if it is not running yet. This also finds the process owner of the pdf, so we can execute forward search later.
*/
fun openFile(pdfFilePath: String, project: Project) {
// Will do nothing if file is already open in Evince
findProcessOwner(pdfFilePath, project)
}
/**
* Execute forward search, highlighting a certain line in Evince.
* If a pdf file is given, it will execute FindDocument and open the pdf file again to find the latest process owner. If the pdf file is already open, this will do nothing.
*
* @param pdfPath Full path to a pdf file.
* @param sourceFilePath Full path to the LaTeX source file.
* @param line Line number in the source file to highlight in the pdf.
*/
override fun forwardSearch(pdfPath: String?, sourceFilePath: String, line: Int, project: Project, focusAllowed: Boolean) {
// If we are not allowed to change focus, we cannot open the pdf or do forward search because this will always change focus with Evince
if (!focusAllowed) {
return
}
if (pdfPath != null) {
findProcessOwner(pdfPath, project)
}
if (processOwner != null) {
// Theoretically we should use the Java D-Bus bindings as well to call SyncView, but that did
// not succeed with a NoReply exception, so we will execute a command via the shell
val command = "gdbus call --session --dest $processOwner --object-path /org/gnome/evince/Window/0 --method org.gnome.evince.Window.SyncView $sourceFilePath '($line, 1)' 0"
runCommand("bash", "-c", command)
}
else {
// If the user used the forward search menu action
if (pdfPath == null) {
Notification("LaTeX", "Could not execute forward search", "Please make sure you have compiled the document first, and that your path does not contain spaces.", NotificationType.ERROR).notify(project)
}
else {
throw TeXception("Could not execute forward search with Evince because something went wrong when finding the pdf file at $pdfPath")
}
}
}
/**
* Execute FindDocument on the D-Bus in order to find the process owner of the given pdf file, i.e. the process name which we can use for
* forward/inverse search.
* The value found will be saved for later use.
*
* @param pdfFilePath Full path to the pdf file to find the owner of.
*/
private fun findProcessOwner(pdfFilePath: String, project: Project) {
// Initialize a session bus
val connection = DBusConnection.getConnection(DBusConnection.DBusBusType.SESSION)
// Get the Daemon object using its bus name and object path
val daemon = connection.getRemoteObject(evinceDaemonName, evinceDaemonPath, Daemon::class.java)
// Call the method on the D-Bus by using the function we defined in the Daemon interface
// Catch a NoReply, because it is unknown why Evince cannot start so we don't try to fix that
try {
processOwner = daemon.FindDocument("file://$pdfFilePath", true)
}
catch (ignored: NoReply) {}
catch (e: ServiceUnknown) {
Notification("LaTeX", "Cannot communicate to Evince", "Please update Evince and then try again.", NotificationType.ERROR).notify(project)
}
}
}