Commit 735e1778 authored by Philipp Erhardt's avatar Philipp Erhardt

Implement config file

I use QSettings in ini format. See the qt documentation to learn about
file locations. If there is no config found the default values are used.

Also, fixed memory leaks.
parent 5f4eed24
#include <QAction>
#include <QStringListIterator>
#include <QKeySequence>
#include <QCoreApplication>
#include <QString>
#include <QPainter>
#include <iostream>
#include "canvas.h" #include "canvas.h"
#include "viewer.h" #include "viewer.h"
#include "layout.h" #include "layout.h"
#include "resourcemanager.h" #include "resourcemanager.h"
#include "search.h" #include "search.h"
#include "gotoline.h" #include "gotoline.h"
#include "config.h"
using namespace std; using namespace std;
// TODO put in a config source file Canvas::Canvas(Viewer *v, QWidget *parent) :
#define MOUSE_WHEEL_FACTOR 120 // (qt-)delta for turning the mouse wheel 1 click
#define SMOOTH_SCROLL_DELTA 30 // pixel scroll offset
Canvas::Canvas(Viewer *v, int start_page, QWidget *parent) :
QWidget(parent), QWidget(parent),
viewer(v), viewer(v),
draw_overlay(true), draw_overlay(true),
...@@ -21,52 +24,42 @@ Canvas::Canvas(Viewer *v, int start_page, QWidget *parent) : ...@@ -21,52 +24,42 @@ Canvas::Canvas(Viewer *v, int start_page, QWidget *parent) :
setFocusPolicy(Qt::StrongFocus); setFocusPolicy(Qt::StrongFocus);
layout = new PresentationLayout(viewer->get_res()); layout = new PresentationLayout(viewer->get_res());
layout->scroll_page(start_page, false); // apply start option
// load config options
CFG *config = CFG::get_instance();
// apply start option
layout->scroll_page(config->get_value("start_page").toInt(), false);
mouse_wheel_factor = config->get_value("mouse_wheel_factor").toInt();
smooth_scroll_delta = config->get_value("smooth_scroll_delta").toInt();
// setup keys
add_action("set_presentation_layout", SLOT(set_presentation_layout()));
add_action("set_grid_layout", SLOT(set_grid_layout()));
add_action("page_down", SLOT(page_down()));
add_action("page_up", SLOT(page_up()));
add_action("page_first", SLOT(page_first()));
add_action("page_last", SLOT(page_last()));
add_action("focus_goto", SLOT(focus_goto()));
add_action("auto_smooth_up", SLOT(auto_smooth_up()));
add_action("auto_smooth_down", SLOT(auto_smooth_down()));
add_action("smooth_left", SLOT(smooth_left()));
add_action("smooth_right", SLOT(smooth_right()));
add_action("zoom_in", SLOT(zoom_in()));
add_action("zoom_out", SLOT(zoom_out()));
add_action("reset_zoom", SLOT(reset_zoom()));
add_action("columns_inc", SLOT(columns_inc()));
add_action("columns_dec", SLOT(columns_dec()));
add_action("toggle_overlay", SLOT(toggle_overlay()));
add_action("quit", SLOT(quit()));
add_action("search", SLOT(search()));
add_action("next_hit", SLOT(next_hit()));
add_action("previous_hit", SLOT(previous_hit()));
add_action("rotate_left", SLOT(rotate_left()));
add_action("rotate_right", SLOT(rotate_right()));
// prints the string representation of a key // prints the string representation of a key
// cerr << QKeySequence(Qt::Key_Equal).toString().toUtf8().constData() << endl; // cerr << QKeySequence(Qt::Key_Equal).toString().toUtf8().constData() << endl;
// key -> function mapping
add_sequence("1", &Canvas::set_presentation_layout);
add_sequence("2", &Canvas::set_grid_layout);
add_sequence("Space", &Canvas::page_down);
add_sequence("PgDown", &Canvas::page_down);
add_sequence("Down", &Canvas::page_down);
add_sequence("Backspace", &Canvas::page_up);
add_sequence("PgUp", &Canvas::page_up);
add_sequence("Up", &Canvas::page_up);
add_sequence("G", &Canvas::page_first);
add_sequence("Shift+G", &Canvas::page_last);
add_sequence("Ctrl+G", &Canvas::focus_goto);
add_sequence("K", &Canvas::auto_smooth_up);
add_sequence("J", &Canvas::auto_smooth_down);
add_sequence("H", &Canvas::smooth_left);
add_sequence("L", &Canvas::smooth_right);
add_sequence("+", &Canvas::zoom_in);
add_sequence("=", &Canvas::zoom_in);
add_sequence("-", &Canvas::zoom_out);
add_sequence("Z", &Canvas::reset_zoom);
add_sequence("]", &Canvas::columns_inc);
add_sequence("[", &Canvas::columns_dec);
add_sequence("T", &Canvas::toggle_overlay);
add_sequence("Q", &Canvas::quit);
add_sequence("W,E,E,E", &Canvas::quit); // just messing around :)
add_sequence("/", &Canvas::search);
add_sequence("N", &Canvas::next_hit);
add_sequence("Shift+N", &Canvas::previous_hit);
add_sequence("u", &Canvas::rotate_left);
add_sequence("i", &Canvas::rotate_right);
goto_line = new GotoLine(viewer->get_res()->get_page_count(), this); goto_line = new GotoLine(viewer->get_res()->get_page_count(), this);
goto_line->hide(); // TODO why is it shown by default? goto_line->hide(); // TODO why is it shown by default?
connect(goto_line, SIGNAL(returnPressed()), this, SLOT(goto_page()), Qt::UniqueConnection); connect(goto_line, SIGNAL(returnPressed()), this, SLOT(goto_page()), Qt::UniqueConnection);
...@@ -89,27 +82,20 @@ void Canvas::reload() { ...@@ -89,27 +82,20 @@ void Canvas::reload() {
update(); update();
} }
void Canvas::add_sequence(QString key, func_t action) { void Canvas::add_action(const char *action, const char *slot) {
QKeySequence s(key); QStringListIterator i(CFG::get_instance()->get_keys(action));
sequences[s] = action; while (i.hasNext()) {
grabShortcut(s, Qt::WidgetShortcut); QAction *a = new QAction(this);
a->setShortcut(QKeySequence(i.next()));
addAction(a);
connect(a, SIGNAL(triggered()), this, slot);
}
} }
const Layout *Canvas::get_layout() const { const Layout *Canvas::get_layout() const {
return layout; return layout;
} }
bool Canvas::event(QEvent *event) {
if (event->type() == QEvent::Shortcut) {
QShortcutEvent *s = static_cast<QShortcutEvent*>(event);
if (sequences.find(s->key()) != sequences.end()) {
(this->*sequences[s->key()])();
}
return true;
}
return QWidget::event(event);
}
void Canvas::paintEvent(QPaintEvent * /*event*/) { void Canvas::paintEvent(QPaintEvent * /*event*/) {
#ifdef DEBUG #ifdef DEBUG
cerr << "redraw" << endl; cerr << "redraw" << endl;
...@@ -172,7 +158,7 @@ void Canvas::wheelEvent(QWheelEvent *event) { ...@@ -172,7 +158,7 @@ void Canvas::wheelEvent(QWheelEvent *event) {
update(); update();
} }
} else { } else {
if (layout->scroll_page(-d / MOUSE_WHEEL_FACTOR)) { if (layout->scroll_page(-d / mouse_wheel_factor)) {
update(); update();
} }
} }
...@@ -253,26 +239,26 @@ void Canvas::auto_smooth_down() { ...@@ -253,26 +239,26 @@ void Canvas::auto_smooth_down() {
} }
void Canvas::smooth_up() { void Canvas::smooth_up() {
if (layout->scroll_smooth(0, SMOOTH_SCROLL_DELTA)) { if (layout->scroll_smooth(0, smooth_scroll_delta)) {
update(); update();
} }
} }
void Canvas::smooth_down() { void Canvas::smooth_down() {
if (layout->scroll_smooth(0, -SMOOTH_SCROLL_DELTA)) { if (layout->scroll_smooth(0, -smooth_scroll_delta)) {
update(); update();
} }
} }
void Canvas::smooth_left() { void Canvas::smooth_left() {
if (layout->scroll_smooth(SMOOTH_SCROLL_DELTA, 0)) { if (layout->scroll_smooth(smooth_scroll_delta, 0)) {
update(); update();
} }
} }
void Canvas::smooth_right() { void Canvas::smooth_right() {
if (layout->scroll_smooth(-SMOOTH_SCROLL_DELTA, 0)) { if (layout->scroll_smooth(-smooth_scroll_delta, 0)) {
update(); update();
} }
} }
......
...@@ -2,18 +2,10 @@ ...@@ -2,18 +2,10 @@
#define CANVAS_H #define CANVAS_H
#include <QWidget> #include <QWidget>
#include <QImage>
#include <QPainter>
#include <QPaintEvent> #include <QPaintEvent>
#include <QString>
//#include <QKeyEvent>
#include <QMouseEvent> #include <QMouseEvent>
#include <QWheelEvent> #include <QWheelEvent>
#include <QCoreApplication>
#include <QResizeEvent> #include <QResizeEvent>
#include <QKeySequence>
#include <iostream>
#include <map>
#include <list> #include <list>
#include <sys/socket.h> #include <sys/socket.h>
...@@ -27,10 +19,8 @@ class GotoLine; ...@@ -27,10 +19,8 @@ class GotoLine;
class Canvas : public QWidget { class Canvas : public QWidget {
Q_OBJECT Q_OBJECT
typedef void (Canvas::*func_t)();
public: public:
Canvas(Viewer *v, int start_page = 0, QWidget *parent = 0); Canvas(Viewer *v, QWidget *parent = 0);
~Canvas(); ~Canvas();
bool is_valid() const; bool is_valid() const;
...@@ -40,10 +30,7 @@ public: ...@@ -40,10 +30,7 @@ public:
protected: protected:
// QT event handling // QT event handling
bool event(QEvent *event);
void paintEvent(QPaintEvent *event); void paintEvent(QPaintEvent *event);
// void keyPressEvent(QKeyEvent *event);
void mousePressEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event);
...@@ -58,7 +45,6 @@ private slots: ...@@ -58,7 +45,6 @@ private slots:
void page_rendered(int page); void page_rendered(int page);
void goto_page(); void goto_page();
private:
// primitive actions // primitive actions
void set_presentation_layout(); void set_presentation_layout();
void set_grid_layout(); void set_grid_layout();
...@@ -86,16 +72,14 @@ private: ...@@ -86,16 +72,14 @@ private:
void rotate_left(); void rotate_left();
void rotate_right(); void rotate_right();
void add_sequence(QString key, func_t action); private:
void add_action(const char *action, const char *slot);
Viewer *viewer; Viewer *viewer;
Layout *layout; Layout *layout;
GotoLine *goto_line; GotoLine *goto_line;
// key sequences
std::map<QKeySequence,func_t> sequences;
int mx, my; int mx, my;
int mx_down, my_down; int mx_down, my_down;
...@@ -103,6 +87,9 @@ private: ...@@ -103,6 +87,9 @@ private:
bool valid; bool valid;
// config options
int mouse_wheel_factor;
int smooth_scroll_delta;
}; };
#endif #endif
......
#include "config.h"
#include <QHashIterator>
CFG::CFG() :
settings(QSettings::IniFormat, QSettings::UserScope, "katarakt") {
// TODO warn about invalid user input
settings.beginGroup("Settings");
// canvas options
defaults["mouse_wheel_factor"] = 120; // (qt-)delta for turning the mouse wheel 1 click
defaults["smooth_scroll_delta"] = 30; // pixel scroll offset
// layout options
defaults["useless_gap"] = 2;
defaults["min_page_width"] = 150;
defaults["min_zoom"] = -14;
defaults["max_zoom"] = 30;
defaults["zoom_factor"] = 0.05;
defaults["prefetch_count"] = 3;
defaults["search_padding"] = 0.2; // must be <= 0.5
// resourcemanager options
defaults["smooth_downscaling"] = true; // filter when creating thumbnail image
defaults["thumbnail_size"] = 32;
// search options
defaults["rect_expansion"] = 2;
settings.endGroup();
settings.beginGroup("keys");
// canvas keys
keys["set_presentation_layout"] = QStringList() << "1";
keys["set_grid_layout"] = QStringList() << "2";
keys["page_down"] = QStringList() << "Space" << "PgDown" << "Down";
keys["page_up"] = QStringList() << "Backspace" << "PgUp" << "Up";
keys["page_first"] = QStringList() << "G";
keys["page_last"] = QStringList() << "Shift+G";
keys["focus_goto"] = QStringList() << "Ctrl+G";
keys["auto_smooth_up"] = QStringList() << "K";
keys["auto_smooth_down"] = QStringList() << "J";
keys["smooth_left"] = QStringList() << "H";
keys["smooth_right"] = QStringList() << "L";
keys["zoom_in"] = QStringList() << "=" << "+";
keys["zoom_out"] = QStringList() << "-";
keys["reset_zoom"] = QStringList() << "Z";
keys["columns_inc"] = QStringList() << "]";
keys["columns_dec"] = QStringList() << "[";
keys["toggle_overlay"] = QStringList() << "T";
keys["quit"] = QStringList() << "Q" << "W,E,E,E";
keys["search"] = QStringList() << "/";
keys["next_hit"] = QStringList() << "N";
keys["previous_hit"] = QStringList() << "Shift+N";
keys["rotate_left"] = QStringList() << "U";
keys["rotate_right"] = QStringList() << "I";
// viewer keys
keys["toggle_fullscreen"] = QStringList() << "F";
keys["close_search"] = QStringList() << "Esc";
keys["reload"] = QStringList() << "R";
settings.endGroup();
}
CFG::CFG(const CFG &/*other*/) {
}
CFG &CFG::operator=(const CFG &/*other*/) {
return *this;
}
CFG::~CFG() {
// do not write temporary options to disk
settings.remove("start_page");
settings.remove("fullscreen");
// set_defaults(); // uncomment to create ini file with all the default settings
}
void CFG::set_defaults() {
settings.beginGroup("Settings");
QHashIterator<QString,QVariant> it(defaults);
while (it.hasNext()) {
it.next();
settings.setValue(it.key(), it.value());
}
settings.endGroup();
settings.beginGroup("Keys");
QHashIterator<QString,QStringList> i2(keys);
while (i2.hasNext()) {
i2.next();
settings.setValue(i2.key(), i2.value());
}
settings.endGroup();
}
CFG *CFG::get_instance() {
static CFG instance;
return &instance;
}
QVariant CFG::get_value(const char *key) const {
return settings.value(QString("Settings/") + key, defaults[key]);
}
void CFG::set_value(const char *key, QVariant value) {
settings.setValue(QString("Settings/") + key, value);
}
QStringList CFG::get_keys(const char *action) const {
return settings.value(QString("Keys/") + action, keys[action]).toStringList();
}
#ifndef CONFIG_H
#define CONFIG_H
#include <QSettings>
#include <QHash>
#include <QString>
#include <QVariant>
#include <QStringList>
class CFG {
private:
CFG();
CFG(const CFG &other);
CFG &operator=(const CFG &other);
~CFG();
void set_defaults();
QSettings settings;
QHash<QString,QVariant> defaults;
QHash<QString,QStringList> keys;
public:
static CFG *get_instance();
QVariant get_value(const char *key) const;
void set_value(const char *key, QVariant value);
QStringList get_keys(const char *action) const;
};
#endif
...@@ -7,6 +7,6 @@ CONFIG += qt ...@@ -7,6 +7,6 @@ CONFIG += qt
QMAKE_CXXFLAGS_DEBUG += -DDEBUG QMAKE_CXXFLAGS_DEBUG += -DDEBUG
# Input # Input
HEADERS += layout.h viewer.h canvas.h resourcemanager.h grid.h search.h gotoline.h HEADERS += layout.h viewer.h canvas.h resourcemanager.h grid.h search.h gotoline.h config.h
SOURCES += main.cpp layout.cpp viewer.cpp canvas.cpp resourcemanager.cpp grid.cpp search.cpp gotoline.cpp SOURCES += main.cpp layout.cpp viewer.cpp canvas.cpp resourcemanager.cpp grid.cpp search.cpp gotoline.cpp config.cpp
unix:LIBS += -lpoppler-qt4 unix:LIBS += -lpoppler-qt4
#include <iostream>
#include <QImage>
#include "layout.h" #include "layout.h"
#include "resourcemanager.h" #include "resourcemanager.h"
#include "grid.h" #include "grid.h"
#include "search.h" #include "search.h"
#include "config.h"
using namespace std; using namespace std;
// TODO put in a config source file
#define USELESS_GAP 2
#define MIN_PAGE_WIDTH 150
#define MIN_ZOOM -14
#define MAX_ZOOM 30
#define ZOOM_FACTOR 0.05f
#define PREFETCH_COUNT 3
#define SEARCH_PADDING 0.2 // must be <= 0.5
// rounds a float when afterwards cast to int // rounds a float when afterwards cast to int
// seems to fix the mismatch between calculated page height and actual image height // seems to fix the mismatch between calculated page height and actual image height
#define ROUND(x) ((x) + 0.5f) #define ROUND(x) ((x) + 0.5f)
...@@ -25,6 +19,19 @@ using namespace std; ...@@ -25,6 +19,19 @@ using namespace std;
Layout::Layout(ResourceManager *_res, int _page) : Layout::Layout(ResourceManager *_res, int _page) :
res(_res), page(_page), off_x(0), off_y(0), width(0), height(0), res(_res), page(_page), off_x(0), off_y(0), width(0), height(0),
search_visible(false) { search_visible(false) {
// load config options
CFG *config = CFG::get_instance();
useless_gap = config->get_value("useless_gap").toInt();
min_page_width = config->get_value("min_page_width").toInt();
min_zoom = config->get_value("min_zoom").toInt();
max_zoom = config->get_value("max_zoom").toInt();
zoom_factor = config->get_value("zoom_factor").toFloat();
prefetch_count = config->get_value("prefetch_count").toInt();
search_padding = config->get_value("search_padding").toFloat();
}
Layout::~Layout() {
clear_hits();
} }
int Layout::get_page() const { int Layout::get_page() const {
...@@ -260,7 +267,7 @@ void PresentationLayout::render(QPainter *painter) { ...@@ -260,7 +267,7 @@ void PresentationLayout::render(QPainter *painter) {
} */ } */
// prefetch // prefetch
for (int count = 1; count <= PREFETCH_COUNT; count++) { for (int count = 1; count <= prefetch_count; count++) {
// after current page // after current page
if (res->get_page(page + count, calculate_fit_width(page + count)) != NULL) { if (res->get_page(page + count, calculate_fit_width(page + count)) != NULL) {
res->unlock_page(page + count); res->unlock_page(page + count);
...@@ -270,7 +277,7 @@ void PresentationLayout::render(QPainter *painter) { ...@@ -270,7 +277,7 @@ void PresentationLayout::render(QPainter *painter) {
res->unlock_page(page - count); res->unlock_page(page - count);
} }
} }
res->collect_garbage(page - PREFETCH_COUNT * 3, page + PREFETCH_COUNT * 3); res->collect_garbage(page - prefetch_count * 3, page + prefetch_count * 3);
} }
void PresentationLayout::advance_hit(bool forward) { void PresentationLayout::advance_hit(bool forward) {
...@@ -371,14 +378,14 @@ void GridLayout::set_constants() { ...@@ -371,14 +378,14 @@ void GridLayout::set_constants() {
for (int i = 0; i < grid->get_column_count(); i++) { for (int i = 0; i < grid->get_column_count(); i++) {
used += grid->get_width(i); used += grid->get_width(i);
} }
int available = width - USELESS_GAP * (grid->get_column_count() - 1); int available = width - useless_gap * (grid->get_column_count() - 1);
if (available < MIN_PAGE_WIDTH * grid->get_column_count()) { if (available < min_page_width * grid->get_column_count()) {
available = MIN_PAGE_WIDTH * grid->get_column_count(); available = min_page_width * grid->get_column_count();
} }
size = (float) available / used; size = (float) available / used;
// apply zoom value // apply zoom value
size *= (1 + zoom * ZOOM_FACTOR); size *= (1 + zoom * zoom_factor);
horizontal_page = (page + horizontal_page) % grid->get_column_count(); horizontal_page = (page + horizontal_page) % grid->get_column_count();
page = page / grid->get_column_count() * grid->get_column_count(); page = page / grid->get_column_count() * grid->get_column_count();
...@@ -387,13 +394,13 @@ void GridLayout::set_constants() { ...@@ -387,13 +394,13 @@ void GridLayout::set_constants() {
for (int i = 0; i < grid->get_row_count(); i++) { for (int i = 0; i < grid->get_row_count(); i++) {
total_height += ROUND(grid->get_height(i * grid->get_column_count()) * size); total_height += ROUND(grid->get_height(i * grid->get_column_count()) * size);
} }
total_height += USELESS_GAP * (grid->get_row_count() - 1); total_height += useless_gap * (grid->get_row_count() - 1);
total_width = 0; total_width = 0;
for (int i = 0; i < grid->get_column_count(); i++) { for (int i = 0; i < grid->get_column_count(); i++) {
total_width += grid->get_width(i) * size; total_width += grid->get_width(i) * size;
} }
total_width += USELESS_GAP * (grid->get_column_count() - 1); total_width += useless_gap * (grid->get_column_count() - 1);
// calculate offset for blocking at the right border // calculate offset for blocking at the right border
border_page_w = grid->get_column_count(); border_page_w = grid->get_column_count();
...@@ -405,7 +412,7 @@ void GridLayout::set_constants() { ...@@ -405,7 +412,7 @@ void GridLayout::set_constants() {
border_off_w = width - w; border_off_w = width - w;
break; break;
} }
w += USELESS_GAP; w += useless_gap;
} }
// bottom border // bottom border
border_page_h = grid->get_row_count() * grid->get_column_count(); border_page_h = grid