mirror of
https://git.pwmt.org/pwmt/zathura.git
synced 2024-12-28 20:56:00 +01:00
815c51d066
Signed-off-by: Sebastian Ramacher <sebastian+dev@ramacher.at>
473 lines
12 KiB
C
473 lines
12 KiB
C
/* See LICENSE file for license and copyright information */
|
|
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <math.h>
|
|
#include <gtk/gtk.h>
|
|
#include <girara/session.h>
|
|
#include <girara/utils.h>
|
|
#include <girara/settings.h>
|
|
#include <glib/gi18n.h>
|
|
|
|
#include "links.h"
|
|
#include "utils.h"
|
|
#include "zathura.h"
|
|
#include "internal.h"
|
|
#include "document.h"
|
|
#include "page.h"
|
|
#include "plugin.h"
|
|
|
|
#include <girara/datastructures.h>
|
|
|
|
#define BLOCK_SIZE 64
|
|
|
|
const char*
|
|
file_get_extension(const char* path)
|
|
{
|
|
if (!path) {
|
|
return NULL;
|
|
}
|
|
|
|
unsigned int i = strlen(path);
|
|
for (; i > 0; i--) {
|
|
if (*(path + i) != '.') {
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!i) {
|
|
return NULL;
|
|
}
|
|
|
|
return path + i + 1;
|
|
}
|
|
|
|
bool
|
|
file_valid_extension(zathura_t* zathura, const char* path)
|
|
{
|
|
if (zathura == NULL || path == NULL || zathura->plugins.manager == NULL) {
|
|
return false;
|
|
}
|
|
|
|
const gchar* content_type = g_content_type_guess(path, NULL, 0, NULL);
|
|
if (content_type == NULL) {
|
|
return false;
|
|
}
|
|
|
|
zathura_plugin_t* plugin = zathura_plugin_manager_get_plugin(zathura->plugins.manager, content_type);
|
|
g_free((void*)content_type);
|
|
|
|
return (plugin == NULL) ? false : true;
|
|
}
|
|
|
|
bool
|
|
execute_command(char* const argv[], char** output)
|
|
{
|
|
if (!output) {
|
|
return false;
|
|
}
|
|
|
|
int p[2];
|
|
if (pipe(p)) {
|
|
return -1;
|
|
}
|
|
|
|
pid_t pid = fork();
|
|
|
|
if (pid == -1) { // failure
|
|
return false;
|
|
} else if (pid == 0) { // child
|
|
dup2(p[1], 1);
|
|
close(p[0]);
|
|
|
|
if (execvp(argv[0], argv) == -1) {
|
|
return false;
|
|
}
|
|
} else { // parent
|
|
dup2(p[0], 0);
|
|
close(p[1]);
|
|
|
|
/* read output */
|
|
unsigned int bc = BLOCK_SIZE;
|
|
unsigned int i = 0;
|
|
char* buffer = malloc(sizeof(char) * bc);
|
|
*output = NULL;
|
|
|
|
if (!buffer) {
|
|
close(p[0]);
|
|
return false;
|
|
}
|
|
|
|
char c;
|
|
while (1 == read(p[0], &c, 1)) {
|
|
buffer[i++] = c;
|
|
|
|
if (i == bc) {
|
|
bc += BLOCK_SIZE;
|
|
char* tmp = realloc(buffer, sizeof(char) * bc);
|
|
|
|
if (!tmp) {
|
|
free(buffer);
|
|
close(p[0]);
|
|
return false;
|
|
}
|
|
|
|
buffer = tmp;
|
|
}
|
|
}
|
|
|
|
char* tmp = realloc(buffer, sizeof(char) * (bc + 1));
|
|
if (!tmp) {
|
|
free(buffer);
|
|
close(p[0]);
|
|
return false;
|
|
}
|
|
|
|
buffer = tmp;
|
|
buffer[i] = '\0';
|
|
|
|
*output = buffer;
|
|
|
|
/* wait for child to terminate */
|
|
waitpid(pid, NULL, 0);
|
|
close(p[0]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
document_index_build(GtkTreeModel* model, GtkTreeIter* parent,
|
|
girara_tree_node_t* tree)
|
|
{
|
|
girara_list_t* list = girara_node_get_children(tree);
|
|
GIRARA_LIST_FOREACH(list, girara_tree_node_t*, iter, node)
|
|
zathura_index_element_t* index_element = (zathura_index_element_t*)girara_node_get_data(node);
|
|
|
|
zathura_link_type_t type = zathura_link_get_type(index_element->link);
|
|
zathura_link_target_t target = zathura_link_get_target(index_element->link);
|
|
|
|
gchar* description = NULL;
|
|
if (type == ZATHURA_LINK_GOTO_DEST) {
|
|
description = g_strdup_printf("Page %d", target.page_number + 1);
|
|
} else {
|
|
description = g_strdup(target.value);
|
|
}
|
|
|
|
GtkTreeIter tree_iter;
|
|
gtk_tree_store_append(GTK_TREE_STORE(model), &tree_iter, parent);
|
|
gtk_tree_store_set(GTK_TREE_STORE(model), &tree_iter, 0, index_element->title, 1, description, 2, index_element, -1);
|
|
g_object_weak_ref(G_OBJECT(model), (GWeakNotify) zathura_index_element_free, index_element);
|
|
g_free(description);
|
|
|
|
if (girara_node_get_num_children(node) > 0) {
|
|
document_index_build(model, &tree_iter, node);
|
|
}
|
|
|
|
GIRARA_LIST_FOREACH_END(list, gchar*, iter, name);
|
|
}
|
|
|
|
void
|
|
page_calculate_offset(zathura_t* zathura, zathura_page_t* page, page_offset_t* offset)
|
|
{
|
|
g_return_if_fail(page != NULL);
|
|
g_return_if_fail(offset != NULL);
|
|
GtkWidget* widget = zathura_page_get_widget(zathura, page);
|
|
|
|
g_return_if_fail(gtk_widget_translate_coordinates(widget,
|
|
zathura->ui.page_widget, 0, 0, &(offset->x), &(offset->y)) == true);
|
|
}
|
|
|
|
zathura_rectangle_t rotate_rectangle(zathura_rectangle_t rectangle, unsigned int degree, int height, int width)
|
|
{
|
|
zathura_rectangle_t tmp;
|
|
switch (degree) {
|
|
case 90:
|
|
tmp.x1 = height - rectangle.y2;
|
|
tmp.x2 = height - rectangle.y1;
|
|
tmp.y1 = rectangle.x1;
|
|
tmp.y2 = rectangle.x2;
|
|
break;
|
|
case 180:
|
|
tmp.x1 = width - rectangle.x2;
|
|
tmp.x2 = width - rectangle.x1;
|
|
tmp.y1 = height - rectangle.y2;
|
|
tmp.y2 = height - rectangle.y1;
|
|
break;
|
|
case 270:
|
|
tmp.x1 = rectangle.y1;
|
|
tmp.x2 = rectangle.y2;
|
|
tmp.y1 = width - rectangle.x2;
|
|
tmp.y2 = width - rectangle.x1;
|
|
break;
|
|
default:
|
|
tmp.x1 = rectangle.x1;
|
|
tmp.x2 = rectangle.x2;
|
|
tmp.y1 = rectangle.y1;
|
|
tmp.y2 = rectangle.y2;
|
|
}
|
|
|
|
return tmp;
|
|
}
|
|
|
|
zathura_rectangle_t
|
|
recalc_rectangle(zathura_page_t* page, zathura_rectangle_t rectangle)
|
|
{
|
|
if (page == NULL) {
|
|
goto error_ret;
|
|
}
|
|
|
|
zathura_document_t* document = zathura_page_get_document(page);
|
|
|
|
if (document == NULL) {
|
|
goto error_ret;
|
|
}
|
|
|
|
double page_height = zathura_page_get_height(page);
|
|
double page_width = zathura_page_get_width(page);
|
|
double scale = zathura_document_get_scale(document);
|
|
|
|
zathura_rectangle_t tmp;
|
|
|
|
switch (zathura_document_get_rotation(document)) {
|
|
case 90:
|
|
tmp.x1 = (page_height - rectangle.y2) * scale;
|
|
tmp.x2 = (page_height - rectangle.y1) * scale;
|
|
tmp.y1 = rectangle.x1 * scale;
|
|
tmp.y2 = rectangle.x2 * scale;
|
|
break;
|
|
case 180:
|
|
tmp.x1 = (page_width - rectangle.x2) * scale;
|
|
tmp.x2 = (page_width - rectangle.x1) * scale;
|
|
tmp.y1 = (page_height - rectangle.y2) * scale;
|
|
tmp.y2 = (page_height - rectangle.y1) * scale;
|
|
break;
|
|
case 270:
|
|
tmp.x1 = rectangle.y1 * scale;
|
|
tmp.x2 = rectangle.y2 * scale;
|
|
tmp.y1 = (page_width - rectangle.x2) * scale;
|
|
tmp.y2 = (page_width - rectangle.x1) * scale;
|
|
break;
|
|
default:
|
|
tmp.x1 = rectangle.x1 * scale;
|
|
tmp.x2 = rectangle.x2 * scale;
|
|
tmp.y1 = rectangle.y1 * scale;
|
|
tmp.y2 = rectangle.y2 * scale;
|
|
}
|
|
|
|
return tmp;
|
|
|
|
error_ret:
|
|
|
|
return rectangle;
|
|
}
|
|
|
|
void
|
|
set_adjustment(GtkAdjustment* adjustment, gdouble value)
|
|
{
|
|
gtk_adjustment_set_value(adjustment, MAX(gtk_adjustment_get_lower(adjustment),
|
|
MIN(gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment), value)));
|
|
}
|
|
|
|
void
|
|
page_calc_height_width(zathura_page_t* page, unsigned int* page_height, unsigned int* page_width, bool rotate)
|
|
{
|
|
g_return_if_fail(page != NULL && page_height != NULL && page_width != NULL);
|
|
|
|
zathura_document_t* document = zathura_page_get_document(page);
|
|
if (document == NULL) {
|
|
return;
|
|
}
|
|
|
|
double height = zathura_page_get_height(page);
|
|
double width = zathura_page_get_width(page);
|
|
double scale = zathura_document_get_scale(document);
|
|
|
|
if (rotate && zathura_document_get_rotation(document) % 180) {
|
|
*page_width = ceil(height * scale);
|
|
*page_height = ceil(width * scale);
|
|
} else {
|
|
*page_width = ceil(width * scale);
|
|
*page_height = ceil(height * scale);
|
|
}
|
|
}
|
|
|
|
void
|
|
zathura_get_document_size(zathura_t* zathura,
|
|
unsigned int cell_height, unsigned int cell_width,
|
|
unsigned int* height, unsigned int* width)
|
|
{
|
|
g_return_if_fail(zathura != NULL && zathura->document != NULL &&
|
|
height != NULL && width != NULL);
|
|
|
|
unsigned int pages_per_row = 1;
|
|
girara_setting_get(zathura->ui.session, "pages-per-row", &pages_per_row);
|
|
if (pages_per_row == 0)
|
|
pages_per_row = 1;
|
|
|
|
unsigned int first_page_column = 1;
|
|
girara_setting_get(zathura->ui.session, "first-page-column", &first_page_column);
|
|
if (first_page_column < 1)
|
|
first_page_column = 1;
|
|
if (first_page_column > pages_per_row)
|
|
first_page_column = (first_page_column - 1) % pages_per_row + 1;
|
|
|
|
int padding = 1;
|
|
girara_setting_get(zathura->ui.session, "page-padding", &padding);
|
|
|
|
double scale = zathura_document_get_scale(zathura->document);
|
|
|
|
cell_height = ceil(cell_height * scale);
|
|
cell_width = ceil(cell_width * scale);
|
|
|
|
*width = pages_per_row * cell_width + (pages_per_row - 1) * padding;
|
|
unsigned int effective_number_of_pages =
|
|
zathura_document_get_number_of_pages(zathura->document) +
|
|
first_page_column - 1;
|
|
unsigned int rows = effective_number_of_pages / pages_per_row +
|
|
(effective_number_of_pages % pages_per_row ? 1 : 0);
|
|
*height = rows * cell_height + (rows - 1) * padding;
|
|
}
|
|
|
|
GtkWidget*
|
|
zathura_page_get_widget(zathura_t* zathura, zathura_page_t* page)
|
|
{
|
|
if (zathura == NULL || page == NULL || zathura->pages == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
unsigned int page_number = zathura_page_get_index(page);
|
|
|
|
return zathura->pages[page_number];
|
|
}
|
|
|
|
void
|
|
readjust_view_after_zooming(zathura_t *zathura, float old_zoom, bool delay)
|
|
{
|
|
if (zathura == NULL || zathura->document == NULL) {
|
|
return;
|
|
}
|
|
|
|
GtkScrolledWindow *window = GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view);
|
|
GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(window);
|
|
GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(window);
|
|
|
|
double scale = zathura_document_get_scale(zathura->document);
|
|
gdouble valx = gtk_adjustment_get_value(hadjustment) / old_zoom * scale;
|
|
gdouble valy = gtk_adjustment_get_value(vadjustment) / old_zoom * scale;
|
|
|
|
bool zoom_center = false;
|
|
girara_setting_get(zathura->ui.session, "zoom-center", &zoom_center);
|
|
if (zoom_center) {
|
|
valx += gtk_adjustment_get_page_size(hadjustment) * (scale / old_zoom - 1) / 2;
|
|
}
|
|
|
|
if (delay == true) {
|
|
position_set_delayed(zathura, valx, valy);
|
|
} else {
|
|
set_adjustment(hadjustment, valx);
|
|
set_adjustment(vadjustment, valy);
|
|
}
|
|
}
|
|
|
|
void
|
|
document_draw_search_results(zathura_t* zathura, bool value)
|
|
{
|
|
if (zathura == NULL || zathura->document == NULL || zathura->pages == NULL) {
|
|
return;
|
|
}
|
|
|
|
unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
|
|
for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
|
|
g_object_set(zathura->pages[page_id], "draw-search-results", (value == true) ? TRUE : FALSE, NULL);
|
|
}
|
|
}
|
|
|
|
char*
|
|
zathura_get_version_string(zathura_t* zathura, bool markup)
|
|
{
|
|
if (zathura == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
GString* string = g_string_new(NULL);
|
|
|
|
/* zathura version */
|
|
char* zathura_version = g_strdup_printf("zathura %d.%d.%d", ZATHURA_VERSION_MAJOR, ZATHURA_VERSION_MINOR, ZATHURA_VERSION_REV);
|
|
g_string_append(string, zathura_version);
|
|
g_free(zathura_version);
|
|
|
|
char* format = (markup == true) ? "\n<i>(plugin)</i> %s (%d.%d.%d) <i>(%s)</i>" : "\n(plugin) %s (%d.%d.%d) (%s)";
|
|
|
|
/* plugin information */
|
|
girara_list_t* plugins = zathura_plugin_manager_get_plugins(zathura->plugins.manager);
|
|
if (plugins != NULL) {
|
|
GIRARA_LIST_FOREACH(plugins, zathura_plugin_t*, iter, plugin)
|
|
char* name = zathura_plugin_get_name(plugin);
|
|
zathura_plugin_version_t version = zathura_plugin_get_version(plugin);
|
|
char* text = g_strdup_printf(format,
|
|
(name == NULL) ? "-" : name,
|
|
version.major,
|
|
version.minor,
|
|
version.rev,
|
|
zathura_plugin_get_path(plugin)
|
|
);
|
|
g_string_append(string, text);
|
|
g_free(text);
|
|
GIRARA_LIST_FOREACH_END(plugins, zathura_plugin_t*, iter, plugin);
|
|
}
|
|
|
|
char* version = string->str;
|
|
g_string_free(string, FALSE);
|
|
|
|
return version;
|
|
}
|
|
|
|
char*
|
|
replace_substring(const char* string, const char* old, const char* new)
|
|
{
|
|
if (string == NULL || old == NULL || new == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
size_t old_len = strlen(old);
|
|
size_t new_len = strlen(new);
|
|
|
|
/* count occurences */
|
|
unsigned int count = 0;
|
|
unsigned int i = 0;
|
|
|
|
for (i = 0; string[i] != '\0'; i++) {
|
|
if (strstr(&string[i], old) == &string[i]) {
|
|
i += (old_len - 1);
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if (count == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
char* ret = g_malloc0(sizeof(char) * (i - count * old_len + count * new_len + 1));
|
|
i = 0;
|
|
|
|
/* replace */
|
|
while (*string != '\0') {
|
|
if (strstr(string, old) == string) {
|
|
strncpy(&ret[i], new, new_len);
|
|
i += new_len;
|
|
string += old_len;
|
|
} else {
|
|
ret[i++] = *string++;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|