mirror of
git://projects.qi-hardware.com/vido.git
synced 2025-03-14 09:09:13 +02:00
512 lines
14 KiB
C++
512 lines
14 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2009 by Mirko Lindner,,, *
|
|
* vegyraupe@mira *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
|
|
|
|
#include <gtkmm.h>
|
|
#include <zim/file.h>
|
|
#include <zim/search.h>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
|
|
#include "config.h"
|
|
#include "message_dialog.hh"
|
|
#include "vido.hh"
|
|
#include "main_window.hh"
|
|
#include "search_dialog.hh"
|
|
|
|
#include <cxxtools/loginit.h>
|
|
|
|
log_define("vido.main")
|
|
|
|
extern "C" {
|
|
#include "gtkhtml/gtkhtml.h"
|
|
}
|
|
|
|
// std::string content;
|
|
std::string fileName;
|
|
|
|
//TODO this can't be right
|
|
main_window *window2;
|
|
Gtk::ScrolledWindow *scrolled_window2;
|
|
|
|
|
|
Gtk::Widget *html;
|
|
GtkWidget *html_wg;
|
|
std::vector < std::pair< std::string, std::string > > history;
|
|
int position;
|
|
bool historyCall = false;
|
|
// variable to keep url of currently displayed article
|
|
std::string current_url = "";
|
|
|
|
// // // misc functions
|
|
// get zimFile, if not already opened, open file
|
|
const zim::File& get_file()
|
|
{
|
|
static zim::File zimFile;
|
|
|
|
if (!zimFile.good())
|
|
{
|
|
// log_debug("file not initialized:" << fileName);
|
|
zimFile = zim::File(fileName);
|
|
// log_debug("number of articles: " << zimFile.getCountArticles());
|
|
}
|
|
|
|
// log_debug("returning file.");
|
|
return zimFile;
|
|
}
|
|
|
|
|
|
void screenblock(int val){
|
|
|
|
if (val == 1){
|
|
scrolled_window2->remove();
|
|
scrolled_window2->add_label("loading");
|
|
}else if(val == 0){
|
|
scrolled_window2->remove();
|
|
scrolled_window2->add(*html);
|
|
}
|
|
while( Gtk::Main::events_pending() ){
|
|
Gtk::Main::iteration();
|
|
}
|
|
}
|
|
|
|
// display message in gtk window
|
|
void show_message(std::string title, std::string txt)
|
|
{
|
|
message_dialog(window2, title, txt);
|
|
}
|
|
|
|
void show_help()
|
|
{
|
|
log_debug("displaying help");
|
|
std::string title = "Help";
|
|
std::string txt = "Usage: \n";
|
|
txt += "Ctrl + R = Display random article\n";
|
|
txt += "Ctrl + S = Search for article\n";
|
|
txt += "Ctrl + T = Go to articles top\n";
|
|
txt += "Tab = Rotate through links\n";
|
|
txt += "Enter = Activate link\n";
|
|
txt += "Ctrl + H = Display history\n";
|
|
txt += "Ctrl + B = Go back in history\n";
|
|
txt += "Ctrl + F = Go forward in history\n";
|
|
txt += "Ctrl + Q = Quit Vido\n";
|
|
txt += "F1 = Display this help\n";
|
|
show_message(title, txt);
|
|
}
|
|
|
|
// fill gtkhtml widget with new content
|
|
|
|
void fill_gtkhtml(std::string& html_str, std::string url, std::string title){
|
|
log_debug("fill gtkhtml called with " << position << " " << url << " and " << title);
|
|
std::string ccontent;
|
|
ccontent = "<a name=\"top\"></a>" + html_str;
|
|
gtk_html_flush(GTK_HTML(html_wg));
|
|
gtk_html_load_from_string(GTK_HTML(html_wg), ccontent.c_str(), -1);
|
|
current_url = url;
|
|
if ((url != "") && title != ""){
|
|
if ((!historyCall)){
|
|
if(position != 0){
|
|
std::vector < std::pair< std::string, std::string > >::iterator iterator = history.begin();
|
|
log_debug("history size: " << history.size());
|
|
for (int d=0; d < history.size(); d++){
|
|
log_debug("history entry: " << d << " " << history[d].first);
|
|
}
|
|
for (int m=history.size()+position; m <= history.size(); m++){
|
|
log_debug("erasing history: number: "<< m << "title: " << history[m].first);
|
|
history.erase(iterator+m);
|
|
}
|
|
}
|
|
log_debug("adding " << title << " to history \n" << "position: " << position);
|
|
history.push_back( std::make_pair( url, title ) );
|
|
if (history.size() == 11){
|
|
history.erase(history.begin());
|
|
}
|
|
log_debug("history size after stuff: " << history.size());
|
|
}else{
|
|
// if (!historyCall){
|
|
// log_debug("clearing history");
|
|
// std::vector < std::pair< std::string, std::string > >::iterator iterator = history.begin();
|
|
// for (int m=0; m < position; m++){
|
|
// history.erase(iterator+m);
|
|
// }
|
|
// }else{
|
|
historyCall = false;
|
|
// }
|
|
}
|
|
|
|
}
|
|
// gtk_html_get_selection_html(GTK_HTML(html_wg), )
|
|
while( Gtk::Main::events_pending() ){
|
|
Gtk::Main::iteration();
|
|
}
|
|
screenblock(0);
|
|
|
|
window2->connect_all();
|
|
|
|
}
|
|
|
|
void show_history()
|
|
{
|
|
screenblock(1);
|
|
std::string res, url, title;
|
|
res += "<ul style=\"list-style-type:none;\">";
|
|
log_debug("history size: " << history.size());
|
|
for (int d=0; d < history.size(); d++){
|
|
log_debug("history entry: " << d << " " << history[d].first);
|
|
}
|
|
for(int i=history.size()-1; i >= 0; i--)
|
|
{
|
|
log_debug("entry: " << i);
|
|
#if HAVE_ZIM_QUNICODE_H
|
|
res += "<li><a href=" + history[i].first + ">" + history[i].second + "</a></li>";
|
|
#else
|
|
res += "<li><a href='" + history[i].first + "'>" + history[i].second + "</a>";
|
|
if (position == i){
|
|
res += "<--";
|
|
}
|
|
res += "</li>";
|
|
#endif
|
|
|
|
}
|
|
res += "</ul>";
|
|
url = "";
|
|
title = "";
|
|
|
|
fill_gtkhtml(res, url, title);
|
|
|
|
}
|
|
|
|
void history_jump(int jumper){
|
|
int new_rel_pos = position + jumper;
|
|
log_debug("new_rel_pos = " << new_rel_pos);
|
|
int new_pos = history.size() + new_rel_pos;
|
|
log_debug("new_pos = " << new_pos);
|
|
log_debug("history size = " << history.size());
|
|
if ((new_pos > 0) && (new_pos <= history.size())){
|
|
// position = new_rel_pos;
|
|
historyCall = true;
|
|
std::string url = history[new_pos-1].first;
|
|
log_debug("url is = " << url);
|
|
// const char* uri = url.c_str();
|
|
// getArticleFromUrl(const_cast<char*>(url.c_str()), 1);
|
|
const char* uri = url.c_str();
|
|
getArticleFromUrl(uri, new_rel_pos);
|
|
position = new_rel_pos;
|
|
}else{
|
|
log_debug("jump not possible");
|
|
window2->connect_all();
|
|
}
|
|
}
|
|
|
|
//UNUSED
|
|
// // copy article html from while loop into global variable
|
|
// void set_article(const std::string& txt)
|
|
// {
|
|
// content = txt;
|
|
// }
|
|
|
|
// // // externally called functions
|
|
|
|
|
|
// // display random article
|
|
void show_random()
|
|
{
|
|
// window2->disconnect_all();
|
|
|
|
screenblock(1);
|
|
// log_debug("random called. window all disconnected");
|
|
zim::File m = get_file();
|
|
zim::Article article;
|
|
do
|
|
{
|
|
unsigned int seed = static_cast<unsigned int>(time(0));
|
|
zim::size_type idx = static_cast<zim::size_type>(static_cast<double>(m.getCountArticles()) * rand_r(&seed) / RAND_MAX);
|
|
|
|
article = m.getArticle(idx);
|
|
|
|
//loop in case article is redirect
|
|
do
|
|
{
|
|
article = article.getRedirectArticle();
|
|
}while(article.isRedirect());
|
|
|
|
}while(article.getUrl() == current_url);
|
|
|
|
std::string res = article.getPage();
|
|
|
|
// log_debug("article size=" << res.size());
|
|
// log_debug("article title=" << article.getTitle());
|
|
// log_debug("article namespace=" << article.getNamespace());
|
|
// position = 0;
|
|
fill_gtkhtml(res, article.getUrl(), article.getTitle());
|
|
// log_debug("random called. window all connected");
|
|
|
|
}
|
|
|
|
// // display search dialog
|
|
void search_window(main_window *window_x)
|
|
{
|
|
// html_color = html_color_new_from_rgb('255','0','0');
|
|
// gtk_html_set_color(GTK_HTML(html_wg),html_color);
|
|
search_dialog(window_x, " ");
|
|
|
|
}
|
|
|
|
// //
|
|
void GoToTop()
|
|
{
|
|
log_debug("go to top called");
|
|
std::string term = "top";
|
|
bool success = gtk_html_jump_to_anchor(GTK_HTML(html_wg), const_cast<char*>(term.c_str()));
|
|
}
|
|
|
|
// // // meta functions
|
|
|
|
// // set displayed html to given url
|
|
// // FIXME: returns several articles and displays on one page ... why?
|
|
void getArticleFromUrl(const gchar *url, int pos)
|
|
{
|
|
// TODO unescape url
|
|
|
|
// // create ZIM file accessor
|
|
zim::File m = get_file();
|
|
// // convert url to string
|
|
std::string term;
|
|
term.assign(url);
|
|
// log_debug("str url" << term << "\n");
|
|
std::string content;
|
|
// // replace '+' signs with spaces in url
|
|
std::replace(term.begin(), term.end(), '+', ' ');
|
|
|
|
// // find and declare namespace
|
|
size_t found;
|
|
size_t found2;
|
|
char ns;
|
|
|
|
found=term.find("/");
|
|
if (found != std::string::npos)
|
|
{
|
|
if (found)
|
|
{
|
|
ns = term[0];
|
|
term.erase(found + 1);
|
|
}
|
|
else
|
|
{
|
|
term.erase(1);
|
|
ns = term[0];
|
|
found2 = term.find("/");
|
|
term.erase(found2 + 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ns = 'A';
|
|
}
|
|
|
|
// // try to retrieve article
|
|
try
|
|
{
|
|
// #if HAVE_ZIM_QUNICODE_H
|
|
// zim::Article article = m.getArticle(ns, zim::QUnicodeString(term));
|
|
// #else
|
|
zim::Article article = m.getArticle(ns, term);
|
|
// #endif
|
|
|
|
if (article.good()) // check if article is really found
|
|
{
|
|
content = article.getPage();
|
|
position = pos;
|
|
fill_gtkhtml(content, article.getUrl(), article.getTitle());
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "article \"" << url << "\" not found" << std::endl;
|
|
show_message("Error", "The article you requested (" + term + ") was not found.");
|
|
}
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
std::cerr << e.what() << std::endl;
|
|
}
|
|
}
|
|
|
|
|
|
// // test functions
|
|
void getArticleFromTitle(const gchar *phrase)
|
|
{
|
|
historyCall = false;
|
|
screenblock(1);
|
|
char ns;
|
|
ns = 'A';
|
|
zim::File z = get_file();
|
|
|
|
zim::Search::Results result;
|
|
zim::Search search(z);
|
|
std::string term(phrase);
|
|
std::string res, url, title;
|
|
search.setSearchLimit(25);
|
|
search.find(result, ns, term);
|
|
if( result.size() == 0)
|
|
{
|
|
show_message("Error", "The article you requested (" + term + ") was not found.");
|
|
screenblock(0);
|
|
}else{
|
|
|
|
if (result.size() == 1){
|
|
log_debug("one article in result");
|
|
|
|
zim::Article article = z.getArticle(result[0].getArticle().getIndex());
|
|
|
|
//loop in case article is redirect
|
|
do
|
|
{
|
|
article = article.getRedirectArticle();
|
|
}while(article.isRedirect());
|
|
|
|
res = article.getPage(false, 10);
|
|
url = article.getUrl();
|
|
title = article.getTitle();
|
|
log_debug("set article from title position = 0");
|
|
position = 0;
|
|
}
|
|
else
|
|
{
|
|
log_debug("more than one article in result");
|
|
for (unsigned i = 0; i < result.size(); ++i)
|
|
{
|
|
// #if HAVE_ZIM_QUNICODE_H
|
|
// res += "<li><a href=" + result[i].getArticle().getLongUrl().toXML() + ">" + result[i].getArticle().getLongUrl().toXML() + "</a></li>";
|
|
// #else
|
|
res += "<li><a href='" + result[i].getArticle().getUrl() + "'>" + result[i].getArticle().getUrl() + "</a></li>";
|
|
// #endif
|
|
}
|
|
url = "";
|
|
title = "";
|
|
|
|
}
|
|
|
|
fill_gtkhtml(res, url, title);
|
|
}
|
|
}
|
|
|
|
// // // window response functions
|
|
|
|
// // open requested link
|
|
// FIXME show error window when dead-link requested
|
|
bool on_link_clicked(GtkHTML *html, const gchar *url)
|
|
{
|
|
std::string term(url);
|
|
size_t found;
|
|
found=term.find("#");
|
|
if (found != std::string::npos){
|
|
|
|
if (found == 0){
|
|
term.erase(0, 1);
|
|
bool success = gtk_html_jump_to_anchor(GTK_HTML(html_wg), const_cast<char*>(term.c_str()));
|
|
log_debug("jump to " << term << "worked: " << success);
|
|
}else{
|
|
term = term.substr(0, found);
|
|
//TODO make "set_html" func accept anchor name
|
|
const char* uri = term.c_str();
|
|
getArticleFromUrl(uri, position);
|
|
}
|
|
|
|
}else{
|
|
getArticleFromUrl(url,position);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void motion( GdkEventMotion *event)
|
|
{
|
|
log_debug("motion");
|
|
}
|
|
|
|
bool scrolled(GtkHTML *cb_html, GtkOrientation orientation, GtkScrollType scroll_type, gfloat position)
|
|
{
|
|
log_debug("scrolled");
|
|
// TODO on any scroll adjust cursor, maybe with:
|
|
//static void gtk_html_adjust_cursor_position (GtkHTML *html)
|
|
}
|
|
|
|
// // main function
|
|
int main(int argc, char **argv)
|
|
{
|
|
try
|
|
{
|
|
log_init();
|
|
|
|
if (argc == 2)
|
|
{
|
|
fileName = argv[1];
|
|
|
|
log_debug("vido is here file: " << fileName);
|
|
|
|
Gtk::Main kit(argc, argv);
|
|
|
|
main_window window;
|
|
window.add_modal_grab();
|
|
window.set_title("Vido");
|
|
// window.set_border_width(0);
|
|
window.set_default_size(220, 240);
|
|
// window.set_resizable(0);
|
|
window.connect_all();
|
|
html_wg = gtk_html_new();
|
|
window2 = &window;
|
|
html = Glib::wrap(html_wg);
|
|
g_signal_connect( G_OBJECT( html_wg ), "link_clicked", G_CALLBACK( on_link_clicked ), NULL );
|
|
g_signal_connect( G_OBJECT( html_wg ), "scroll", G_CALLBACK( scrolled ), NULL );
|
|
gtk_html_set_caret_mode(GTK_HTML(html_wg),false);
|
|
// gtk_html_adjust_cursor_position(GTK_HTML(html_wg));
|
|
gtk_widget_set_events(html_wg, 0x3FFFFE);
|
|
gtk_signal_connect (GTK_OBJECT (html_wg), "motion_notify_event",
|
|
GTK_SIGNAL_FUNC (motion), NULL);
|
|
Gtk::ScrolledWindow scrolled_window;
|
|
scrolled_window2 = &scrolled_window;
|
|
scrolled_window.set_resize_mode(Gtk::RESIZE_PARENT);
|
|
scrolled_window.add(*html);
|
|
scrolled_window.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
|
|
|
|
window.add(scrolled_window);
|
|
window.show_all();
|
|
position = 0;
|
|
show_random();
|
|
gtk_html_edit_make_cursor_visible(GTK_HTML(html_wg));
|
|
Gtk::Main::run(window);
|
|
}
|
|
else
|
|
{
|
|
std::cout << "usage: " << argv[0] << " [PATH TO ZIM FILE]" << std::endl;
|
|
}
|
|
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
std::cerr << e.what() << std::endl;
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|