Root/
| 1 | /*************************************************************************** |
| 2 | * Copyright (C) 2009 by Mirko Lindner,,, * |
| 3 | * vegyraupe@mira * |
| 4 | * * |
| 5 | * This program is free software; you can redistribute it and/or modify * |
| 6 | * it under the terms of the GNU General Public License as published by * |
| 7 | * the Free Software Foundation; either version 2 of the License, or * |
| 8 | * (at your option) any later version. * |
| 9 | * * |
| 10 | * This program is distributed in the hope that it will be useful, * |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
| 13 | * GNU General Public License for more details. * |
| 14 | * * |
| 15 | * You should have received a copy of the GNU General Public License * |
| 16 | * along with this program; if not, write to the * |
| 17 | * Free Software Foundation, Inc., * |
| 18 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * |
| 19 | ***************************************************************************/ |
| 20 | |
| 21 | |
| 22 | #include <gtkmm.h> |
| 23 | #include <zim/file.h> |
| 24 | #include <zim/search.h> |
| 25 | #include <iostream> |
| 26 | #include <string> |
| 27 | #include <vector> |
| 28 | |
| 29 | |
| 30 | #include "config.h" |
| 31 | #include "message_dialog.hh" |
| 32 | #include "vido.hh" |
| 33 | #include "main_window.hh" |
| 34 | #include "search_dialog.hh" |
| 35 | |
| 36 | #include <cxxtools/loginit.h> |
| 37 | |
| 38 | log_define("vido.main") |
| 39 | |
| 40 | extern "C" { |
| 41 | #include "gtkhtml/gtkhtml.h" |
| 42 | } |
| 43 | |
| 44 | // std::string content; |
| 45 | std::string fileName; |
| 46 | |
| 47 | //TODO this can't be right |
| 48 | main_window *window2; |
| 49 | Gtk::ScrolledWindow *scrolled_window2; |
| 50 | |
| 51 | |
| 52 | Gtk::Widget *html; |
| 53 | GtkWidget *html_wg; |
| 54 | std::vector < std::pair< std::string, std::string > > history; |
| 55 | int position; |
| 56 | bool historyCall = false; |
| 57 | // variable to keep url of currently displayed article |
| 58 | std::string current_url = ""; |
| 59 | |
| 60 | // // // misc functions |
| 61 | // get zimFile, if not already opened, open file |
| 62 | const zim::File& get_file() |
| 63 | { |
| 64 | static zim::File zimFile; |
| 65 | |
| 66 | if (!zimFile.good()) |
| 67 | { |
| 68 | // log_debug("file not initialized:" << fileName); |
| 69 | zimFile = zim::File(fileName); |
| 70 | // log_debug("number of articles: " << zimFile.getCountArticles()); |
| 71 | } |
| 72 | |
| 73 | // log_debug("returning file."); |
| 74 | return zimFile; |
| 75 | } |
| 76 | |
| 77 | |
| 78 | void screenblock(int val){ |
| 79 | |
| 80 | if (val == 1){ |
| 81 | scrolled_window2->remove(); |
| 82 | scrolled_window2->add_label("loading"); |
| 83 | }else if(val == 0){ |
| 84 | scrolled_window2->remove(); |
| 85 | scrolled_window2->add(*html); |
| 86 | } |
| 87 | while( Gtk::Main::events_pending() ){ |
| 88 | Gtk::Main::iteration(); |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | // display message in gtk window |
| 93 | void show_message(std::string title, std::string txt) |
| 94 | { |
| 95 | message_dialog(window2, title, txt); |
| 96 | } |
| 97 | |
| 98 | void show_help() |
| 99 | { |
| 100 | log_debug("displaying help"); |
| 101 | std::string title = "Help"; |
| 102 | std::string txt = "Usage: \n"; |
| 103 | txt += "Ctrl + R = Display random article\n"; |
| 104 | txt += "Ctrl + S = Search for article\n"; |
| 105 | txt += "Ctrl + T = Go to articles top\n"; |
| 106 | txt += "Tab = Rotate through links\n"; |
| 107 | txt += "Enter = Activate link\n"; |
| 108 | txt += "Ctrl + H = Display history\n"; |
| 109 | txt += "Ctrl + B = Go back in history\n"; |
| 110 | txt += "Ctrl + F = Go forward in history\n"; |
| 111 | txt += "Ctrl + Q = Quit Vido\n"; |
| 112 | txt += "F1 = Display this help\n"; |
| 113 | show_message(title, txt); |
| 114 | window2->connect_all(); |
| 115 | } |
| 116 | |
| 117 | // fill gtkhtml widget with new content |
| 118 | |
| 119 | void fill_gtkhtml(std::string& html_str, std::string url, std::string title){ |
| 120 | log_debug("fill gtkhtml called with " << position << " " << url << " and " << title); |
| 121 | std::string ccontent; |
| 122 | ccontent = "<a name=\"top\"></a>" + html_str; |
| 123 | gtk_html_flush(GTK_HTML(html_wg)); |
| 124 | gtk_html_load_from_string(GTK_HTML(html_wg), ccontent.c_str(), -1); |
| 125 | current_url = url; |
| 126 | if ((url != "") && title != ""){ |
| 127 | if ((!historyCall)){ |
| 128 | if(position != 0){ |
| 129 | std::vector < std::pair< std::string, std::string > >::iterator iterator = history.begin(); |
| 130 | log_debug("history size: " << history.size()); |
| 131 | for (int d=0; d < history.size(); d++){ |
| 132 | log_debug("history entry: " << d << " " << history[d].first); |
| 133 | } |
| 134 | for (int m=history.size()+position; m <= history.size(); m++){ |
| 135 | log_debug("erasing history: number: "<< m << "title: " << history[m].first); |
| 136 | history.erase(iterator+m); |
| 137 | } |
| 138 | } |
| 139 | log_debug("adding " << title << " to history \n" << "position: " << position); |
| 140 | history.push_back( std::make_pair( url, title ) ); |
| 141 | if (history.size() == 11){ |
| 142 | history.erase(history.begin()); |
| 143 | } |
| 144 | log_debug("history size after stuff: " << history.size()); |
| 145 | }else{ |
| 146 | // if (!historyCall){ |
| 147 | // log_debug("clearing history"); |
| 148 | // std::vector < std::pair< std::string, std::string > >::iterator iterator = history.begin(); |
| 149 | // for (int m=0; m < position; m++){ |
| 150 | // history.erase(iterator+m); |
| 151 | // } |
| 152 | // }else{ |
| 153 | historyCall = false; |
| 154 | // } |
| 155 | } |
| 156 | |
| 157 | } |
| 158 | // gtk_html_get_selection_html(GTK_HTML(html_wg), ) |
| 159 | while( Gtk::Main::events_pending() ){ |
| 160 | Gtk::Main::iteration(); |
| 161 | } |
| 162 | screenblock(0); |
| 163 | |
| 164 | window2->connect_all(); |
| 165 | |
| 166 | } |
| 167 | |
| 168 | void show_history() |
| 169 | { |
| 170 | screenblock(1); |
| 171 | std::string res, url, title; |
| 172 | res += "<ul style=\"list-style-type:none;\">"; |
| 173 | log_debug("history size: " << history.size()); |
| 174 | for (int d=0; d < history.size(); d++){ |
| 175 | log_debug("history entry: " << d << " " << history[d].first); |
| 176 | } |
| 177 | for(int i=history.size()-1; i >= 0; i--) |
| 178 | { |
| 179 | log_debug("entry: " << i); |
| 180 | #if HAVE_ZIM_QUNICODE_H |
| 181 | res += "<li><a href=" + history[i].first + ">" + history[i].second + "</a></li>"; |
| 182 | #else |
| 183 | res += "<li><a href='" + history[i].first + "'>" + history[i].second + "</a>"; |
| 184 | if (position == i){ |
| 185 | res += "<--"; |
| 186 | } |
| 187 | res += "</li>"; |
| 188 | #endif |
| 189 | |
| 190 | } |
| 191 | res += "</ul>"; |
| 192 | url = ""; |
| 193 | title = ""; |
| 194 | |
| 195 | fill_gtkhtml(res, url, title); |
| 196 | |
| 197 | } |
| 198 | |
| 199 | void history_jump(int jumper){ |
| 200 | int new_rel_pos = position + jumper; |
| 201 | log_debug("new_rel_pos = " << new_rel_pos); |
| 202 | int new_pos = history.size() + new_rel_pos; |
| 203 | log_debug("new_pos = " << new_pos); |
| 204 | log_debug("history size = " << history.size()); |
| 205 | if ((new_pos > 0) && (new_pos <= history.size())){ |
| 206 | // position = new_rel_pos; |
| 207 | historyCall = true; |
| 208 | std::string url = history[new_pos-1].first; |
| 209 | log_debug("url is = " << url); |
| 210 | // const char* uri = url.c_str(); |
| 211 | // getArticleFromUrl(const_cast<char*>(url.c_str()), 1); |
| 212 | const char* uri = url.c_str(); |
| 213 | getArticleFromUrl(uri, new_rel_pos); |
| 214 | position = new_rel_pos; |
| 215 | }else{ |
| 216 | log_debug("jump not possible"); |
| 217 | window2->connect_all(); |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | //UNUSED |
| 222 | // // copy article html from while loop into global variable |
| 223 | // void set_article(const std::string& txt) |
| 224 | // { |
| 225 | // content = txt; |
| 226 | // } |
| 227 | |
| 228 | // // // externally called functions |
| 229 | |
| 230 | |
| 231 | // // display random article |
| 232 | void show_random() |
| 233 | { |
| 234 | // window2->disconnect_all(); |
| 235 | |
| 236 | screenblock(1); |
| 237 | // log_debug("random called. window all disconnected"); |
| 238 | zim::File m = get_file(); |
| 239 | zim::Article article; |
| 240 | do |
| 241 | { |
| 242 | unsigned int seed = static_cast<unsigned int>(time(0)); |
| 243 | zim::size_type idx = static_cast<zim::size_type>(static_cast<double>(m.getCountArticles()) * rand_r(&seed) / RAND_MAX); |
| 244 | |
| 245 | article = m.getArticle(idx); |
| 246 | |
| 247 | //loop in case article is redirect |
| 248 | do |
| 249 | { |
| 250 | article = article.getRedirectArticle(); |
| 251 | }while(article.isRedirect()); |
| 252 | |
| 253 | }while(article.getUrl() == current_url); |
| 254 | |
| 255 | std::string res = article.getPage(); |
| 256 | |
| 257 | // log_debug("article size=" << res.size()); |
| 258 | // log_debug("article title=" << article.getTitle()); |
| 259 | // log_debug("article namespace=" << article.getNamespace()); |
| 260 | // position = 0; |
| 261 | fill_gtkhtml(res, article.getUrl(), article.getTitle()); |
| 262 | // log_debug("random called. window all connected"); |
| 263 | |
| 264 | } |
| 265 | |
| 266 | // // display search dialog |
| 267 | void search_window(main_window *window_x) |
| 268 | { |
| 269 | // html_color = html_color_new_from_rgb('255','0','0'); |
| 270 | // gtk_html_set_color(GTK_HTML(html_wg),html_color); |
| 271 | search_dialog(window_x, " "); |
| 272 | |
| 273 | } |
| 274 | |
| 275 | // // |
| 276 | void GoToTop() |
| 277 | { |
| 278 | log_debug("go to top called"); |
| 279 | std::string term = "top"; |
| 280 | bool success = gtk_html_jump_to_anchor(GTK_HTML(html_wg), const_cast<char*>(term.c_str())); |
| 281 | window2->connect_all(); |
| 282 | } |
| 283 | |
| 284 | // // // meta functions |
| 285 | |
| 286 | // // set displayed html to given url |
| 287 | // // FIXME: returns several articles and displays on one page ... why? |
| 288 | void getArticleFromUrl(const gchar *url, int pos) |
| 289 | { |
| 290 | // TODO unescape url |
| 291 | |
| 292 | // // create ZIM file accessor |
| 293 | zim::File m = get_file(); |
| 294 | // // convert url to string |
| 295 | std::string term; |
| 296 | term.assign(url); |
| 297 | // log_debug("str url" << term << "\n"); |
| 298 | std::string content; |
| 299 | // // replace '+' signs with spaces in url |
| 300 | std::replace(term.begin(), term.end(), '+', ' '); |
| 301 | |
| 302 | // // find and declare namespace |
| 303 | size_t found; |
| 304 | size_t found2; |
| 305 | char ns; |
| 306 | |
| 307 | found=term.find("/"); |
| 308 | if (found != std::string::npos) |
| 309 | { |
| 310 | if (found) |
| 311 | { |
| 312 | ns = term[0]; |
| 313 | term.erase(found + 1); |
| 314 | } |
| 315 | else |
| 316 | { |
| 317 | term.erase(1); |
| 318 | ns = term[0]; |
| 319 | found2 = term.find("/"); |
| 320 | term.erase(found2 + 1); |
| 321 | } |
| 322 | } |
| 323 | else |
| 324 | { |
| 325 | ns = 'A'; |
| 326 | } |
| 327 | |
| 328 | // // try to retrieve article |
| 329 | try |
| 330 | { |
| 331 | // #if HAVE_ZIM_QUNICODE_H |
| 332 | // zim::Article article = m.getArticle(ns, zim::QUnicodeString(term)); |
| 333 | // #else |
| 334 | zim::Article article = m.getArticle(ns, term); |
| 335 | // #endif |
| 336 | |
| 337 | if (article.good()) // check if article is really found |
| 338 | { |
| 339 | content = article.getPage(); |
| 340 | position = pos; |
| 341 | fill_gtkhtml(content, article.getUrl(), article.getTitle()); |
| 342 | } |
| 343 | else |
| 344 | { |
| 345 | std::cerr << "article \"" << url << "\" not found" << std::endl; |
| 346 | show_message("Error", "The article you requested (" + term + ") was not found."); |
| 347 | } |
| 348 | } |
| 349 | catch (const std::exception& e) |
| 350 | { |
| 351 | std::cerr << e.what() << std::endl; |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | |
| 356 | // // test functions |
| 357 | void getArticleFromTitle(const gchar *phrase) |
| 358 | { |
| 359 | historyCall = false; |
| 360 | screenblock(1); |
| 361 | char ns; |
| 362 | ns = 'A'; |
| 363 | zim::File z = get_file(); |
| 364 | |
| 365 | zim::Search::Results result; |
| 366 | zim::Search search(z); |
| 367 | std::string term(phrase); |
| 368 | std::string res, url, title; |
| 369 | search.setSearchLimit(25); |
| 370 | search.find(result, ns, term); |
| 371 | if( result.size() == 0) |
| 372 | { |
| 373 | show_message("Error", "The article you requested (" + term + ") was not found."); |
| 374 | screenblock(0); |
| 375 | }else{ |
| 376 | |
| 377 | if (result.size() == 1){ |
| 378 | log_debug("one article in result"); |
| 379 | |
| 380 | zim::Article article = z.getArticle(result[0].getArticle().getIndex()); |
| 381 | |
| 382 | //loop in case article is redirect |
| 383 | do |
| 384 | { |
| 385 | article = article.getRedirectArticle(); |
| 386 | }while(article.isRedirect()); |
| 387 | |
| 388 | res = article.getPage(false, 10); |
| 389 | url = article.getUrl(); |
| 390 | title = article.getTitle(); |
| 391 | log_debug("set article from title position = 0"); |
| 392 | position = 0; |
| 393 | } |
| 394 | else |
| 395 | { |
| 396 | log_debug("more than one article in result"); |
| 397 | for (unsigned i = 0; i < result.size(); ++i) |
| 398 | { |
| 399 | // #if HAVE_ZIM_QUNICODE_H |
| 400 | // res += "<li><a href=" + result[i].getArticle().getLongUrl().toXML() + ">" + result[i].getArticle().getLongUrl().toXML() + "</a></li>"; |
| 401 | // #else |
| 402 | res += "<li><a href='" + result[i].getArticle().getUrl() + "'>" + result[i].getArticle().getUrl() + "</a></li>"; |
| 403 | // #endif |
| 404 | } |
| 405 | url = ""; |
| 406 | title = ""; |
| 407 | |
| 408 | } |
| 409 | |
| 410 | fill_gtkhtml(res, url, title); |
| 411 | } |
| 412 | } |
| 413 | |
| 414 | // // // window response functions |
| 415 | |
| 416 | // // open requested link |
| 417 | // FIXME show error window when dead-link requested |
| 418 | bool on_link_clicked(GtkHTML *html, const gchar *url) |
| 419 | { |
| 420 | std::string term(url); |
| 421 | size_t found; |
| 422 | found=term.find("#"); |
| 423 | if (found != std::string::npos){ |
| 424 | |
| 425 | if (found == 0){ |
| 426 | term.erase(0, 1); |
| 427 | bool success = gtk_html_jump_to_anchor(GTK_HTML(html_wg), const_cast<char*>(term.c_str())); |
| 428 | log_debug("jump to " << term << "worked: " << success); |
| 429 | }else{ |
| 430 | term = term.substr(0, found); |
| 431 | //TODO make "set_html" func accept anchor name |
| 432 | const char* uri = term.c_str(); |
| 433 | getArticleFromUrl(uri, position); |
| 434 | } |
| 435 | |
| 436 | }else{ |
| 437 | getArticleFromUrl(url,position); |
| 438 | } |
| 439 | |
| 440 | return true; |
| 441 | } |
| 442 | |
| 443 | bool scrolled(GtkHTML *cb_html, GtkOrientation orientation, GtkScrollType scroll_type, gfloat position) |
| 444 | { |
| 445 | log_debug("scrolled"); |
| 446 | // TODO on any scroll adjust cursor, maybe with: |
| 447 | //static void gtk_html_adjust_cursor_position (GtkHTML *html) |
| 448 | } |
| 449 | |
| 450 | // // main function |
| 451 | int main(int argc, char **argv) |
| 452 | { |
| 453 | try |
| 454 | { |
| 455 | log_init(); |
| 456 | |
| 457 | if (argc == 2) |
| 458 | { |
| 459 | fileName = argv[1]; |
| 460 | |
| 461 | log_debug("vido is here file: " << fileName); |
| 462 | |
| 463 | Gtk::Main kit(argc, argv); |
| 464 | |
| 465 | main_window window; |
| 466 | window.set_title("Vido"); |
| 467 | // window.set_border_width(0); |
| 468 | window.set_default_size(220, 240); |
| 469 | // window.set_resizable(0); |
| 470 | window.connect_all(); |
| 471 | html_wg = gtk_html_new(); |
| 472 | window2 = &window; |
| 473 | html = Glib::wrap(html_wg); |
| 474 | g_signal_connect( G_OBJECT( html_wg ), "link_clicked", G_CALLBACK( on_link_clicked ), NULL ); |
| 475 | g_signal_connect( G_OBJECT( html_wg ), "scroll", G_CALLBACK( scrolled ), NULL ); |
| 476 | gtk_html_set_caret_mode(GTK_HTML(html_wg),false); |
| 477 | // gtk_html_adjust_cursor_position(GTK_HTML(html_wg)); |
| 478 | Gtk::ScrolledWindow scrolled_window; |
| 479 | scrolled_window2 = &scrolled_window; |
| 480 | scrolled_window.add(*html); |
| 481 | scrolled_window.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); |
| 482 | |
| 483 | window.add(scrolled_window); |
| 484 | window.show_all(); |
| 485 | position = 0; |
| 486 | show_random(); |
| 487 | gtk_html_edit_make_cursor_visible(GTK_HTML(html_wg)); |
| 488 | Gtk::Main::run(window); |
| 489 | } |
| 490 | else |
| 491 | { |
| 492 | std::cout << "usage: " << argv[0] << " [PATH TO ZIM FILE]" << std::endl; |
| 493 | } |
| 494 | |
| 495 | } |
| 496 | catch (const std::exception& e) |
| 497 | { |
| 498 | std::cerr << e.what() << std::endl; |
| 499 | return -1; |
| 500 | } |
| 501 | |
| 502 | } |
| 503 | |
| 504 |
Branches:
development
master
