Skip to content
This repository has been archived by the owner on Aug 14, 2022. It is now read-only.

Commit

Permalink
Merge pull request #225 from thomasjo/evince
Browse files Browse the repository at this point in the history
Add support for Evince with SyncTex. Resolves #162
  • Loading branch information
yitzchak committed Sep 22, 2016
2 parents 78c4bad + f13f533 commit e861f94
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 2 deletions.
8 changes: 8 additions & 0 deletions lib/latex.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ export default class Latex {
if (this.okularExecutableExists()) {
return require('./openers/okular-opener')
}

if (this.evinceExecutableExists()) {
return require('./openers/evince-opener')
}
}

if (this.hasPdfViewerPackage()) {
Expand Down Expand Up @@ -146,6 +150,10 @@ export default class Latex {
return fs.existsSync(atom.config.get('latex.okularPath'))
}

evinceExecutableExists () {
return fs.existsSync(atom.config.get('latex.evincePath'))
}

viewerExecutableExists () {
return fs.existsSync(atom.config.get('latex.viewerPath'))
}
Expand Down
108 changes: 108 additions & 0 deletions lib/openers/evince-opener.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/** @babel */

import Opener from '../opener'
import dbus from 'dbus-native'
import url from 'url'

const EVINCE_OBJECT = '/org/gnome/evince/Evince'
const EVINCE_APPLICATION_INTERFACE = 'org.gnome.evince.Application'

const DAEMON_SERVICE = 'org.gnome.evince.Daemon'
const DAEMON_OBJECT = '/org/gnome/evince/Daemon'
const DAEMON_INTERFACE = 'org.gnome.evince.Daemon'

const WINDOW_INTERFACE = 'org.gnome.evince.Window'

const GTK_APPLICATION_OBJECT = '/org/gtk/Application/anonymous'
const GTK_APPLICATION_INTERFACE = 'org.freedesktop.Application'

function syncSource (uri, point) {
atom.workspace.open(url.parse(uri).pathname).then(editor => editor.setCursorBufferPosition(point))
}

function getInterface (bus, serviceName, objectPath, interfaceName) {
return new Promise((resolve, reject) => {
bus.getInterface(serviceName, objectPath, interfaceName, (error, interfaceInstance) => {
if (error) {
reject(error)
} else {
resolve(interfaceInstance)
}
})
})
}

function getWindowList (evinceApplication) {
return new Promise((resolve, reject) => {
evinceApplication.GetWindowList((error, windowNames) => {
if (error) {
reject(error)
} else {
resolve(windowNames)
}
})
})
}

function findDocument (daemon, filePath) {
return new Promise((resolve, reject) => {
const uri = url.format({ protocol: 'file:', slashes: true, pathname: filePath })
daemon.FindDocument(uri, true, (error, documentName) => {
if (error) {
reject(error)
} else {
resolve(documentName)
}
})
})
}

export default class EvinceOpener extends Opener {
constructor () {
super()
this.bus = dbus.sessionBus()
this.windows = {}
}

async getWindow (filePath, texPath) {
if (this.windows[texPath]) return this.windows[texPath]

// First get the Evince daemon interface so we can find the internal document name
const daemon = await getInterface(this.bus, DAEMON_SERVICE, DAEMON_OBJECT, DAEMON_INTERFACE)
const documentName = await findDocument(daemon, filePath)

// Get the application interface and get the window list of the application
const evinceApplication = await getInterface(this.bus, documentName, EVINCE_OBJECT, EVINCE_APPLICATION_INTERFACE)
const windowNames = await getWindowList(evinceApplication)

// Get the window interface of the of the first (only) window and get the
// GTK/FreeDesktop application interface so we can activate the window
const interfaces = {
evinceWindow: await getInterface(this.bus, documentName, windowNames[0], WINDOW_INTERFACE),
gtkApplication: await getInterface(this.bus, documentName, GTK_APPLICATION_OBJECT, GTK_APPLICATION_INTERFACE)
}

interfaces.evinceWindow.on('SyncSource', syncSource)
interfaces.evinceWindow.on('Closed', () => delete this.windows[texPath])
this.windows[texPath] = interfaces

// This seems to help with future syncs
interfaces.evinceWindow.SyncView(texPath, [0, 0], 0)

return interfaces
}

open (filePath, texPath, lineNumber, callback) {
this.getWindow(filePath, texPath)
.then(interfaces => {
if (!this.shouldOpenInBackground()) {
interfaces.gtkApplication.Activate({})
}
// SyncView seems to want to activate the window sometimes
interfaces.evinceWindow.SyncView(texPath, [lineNumber, 0], 0)
if (callback) callback(0)
}, error => {
if (callback) callback(error)
})
}
}
11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"main": "./lib/main",
"repository": "https://github.com/thomasjo/atom-latex",
"dependencies": {
"dbus-native": "^0.2.0",
"etch": "0.7.1",
"fs-plus": "^2.9.1",
"lodash": "^4.13.1",
Expand Down Expand Up @@ -213,18 +214,24 @@
"default": "/usr/bin/okular",
"order": 18
},
"evincePath": {
"description": "Full application path to Evince (*nix).",
"type": "string",
"default": "/usr/bin/evince",
"order": 19
},
"viewerPath": {
"title": "Custom PDF Viewer Path",
"description": "Full application path to your PDF viewer. Overrides Skim and SumatraPDF options.",
"type": "string",
"default": "",
"order": 19
"order": 20
},
"useMasterFileSearch": {
"description": "Enables naive search for master/root file when building distributed documents. Note that this does not affect the equivalent *Magic Comments* functionality.",
"type": "boolean",
"default": false,
"order": 20
"order": 21
}
}
}

0 comments on commit e861f94

Please sign in to comment.