Root/
Source at commit 93a9bc198d4d8662431ca06dfae0fe560fbccf2f created 14 years 1 month ago. By Mirko Lindner, fix history | |
---|---|
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 | } |
115 | |
116 | // fill gtkhtml widget with new content |
117 | |
118 | void fill_gtkhtml(std::string& html_str, std::string url, std::string title){ |
119 | log_debug("fill gtkhtml called with " << position << " " << url << " and " << title); |
120 | std::string ccontent; |
121 | ccontent = "<a name=\"top\"></a>" + html_str; |
122 | gtk_html_flush(GTK_HTML(html_wg)); |
123 | gtk_html_load_from_string(GTK_HTML(html_wg), ccontent.c_str(), -1); |
124 | current_url = url; |
125 | if ((url != "") && title != ""){ |
126 | if ((!historyCall)){ |
127 | if(position != 0){ |
128 | std::vector < std::pair< std::string, std::string > >::iterator iterator = history.begin(); |
129 | log_debug("history size: " << history.size()); |
130 | for (int d=0; d < history.size(); d++){ |
131 | log_debug("history entry: " << d << " " << history[d].first); |
132 | } |
133 | for (int m=history.size()+position; m <= history.size(); m++){ |
134 | log_debug("erasing history: number: "<< m << "title: " << history[m].first); |
135 | history.erase(iterator+m); |
136 | } |
137 | } |
138 | log_debug("adding " << title << " to history \n" << "position: " << position); |
139 | history.push_back( std::make_pair( url, title ) ); |
140 | if (history.size() == 11){ |
141 | history.erase(history.begin()); |
142 | } |
143 | log_debug("history size after stuff: " << history.size()); |
144 | }else{ |
145 | // if (!historyCall){ |
146 | // log_debug("clearing history"); |
147 | // std::vector < std::pair< std::string, std::string > >::iterator iterator = history.begin(); |
148 | // for (int m=0; m < position; m++){ |
149 | // history.erase(iterator+m); |
150 | // } |
151 | // }else{ |
152 | historyCall = false; |
153 | // } |
154 | } |
155 | |
156 | } |
157 | // gtk_html_get_selection_html(GTK_HTML(html_wg), ) |
158 | while( Gtk::Main::events_pending() ){ |
159 | Gtk::Main::iteration(); |
160 | } |
161 | screenblock(0); |
162 | |
163 | window2->connect_all(); |
164 | |
165 | } |
166 | |
167 | void show_history() |
168 | { |
169 | screenblock(1); |
170 | std::string res, url, title; |
171 | res += "<ul style=\"list-style-type:none;\">"; |
172 | log_debug("history size: " << history.size()); |
173 | for (int d=0; d < history.size(); d++){ |
174 | log_debug("history entry: " << d << " " << history[d].first); |
175 | } |
176 | for(int i=history.size()-1; i >= 0; i--) |
177 | { |
178 | log_debug("entry: " << i); |
179 | #if HAVE_ZIM_QUNICODE_H |
180 | res += "<li><a href=" + history[i].first + ">" + history[i].second + "</a></li>"; |
181 | #else |
182 | res += "<li><a href='" + history[i].first + "'>" + history[i].second + "</a>"; |
183 | if (position == i){ |
184 | res += "<--"; |
185 | } |
186 | res += "</li>"; |
187 | #endif |
188 | |
189 | } |
190 | res += "</ul>"; |
191 | url = ""; |
192 | title = ""; |
193 | |
194 | fill_gtkhtml(res, url, title); |
195 | |
196 | } |
197 | |
198 | void history_jump(int jumper){ |
199 | int new_rel_pos = position + jumper; |
200 | log_debug("new_rel_pos = " << new_rel_pos); |
201 | int new_pos = history.size() + new_rel_pos; |
202 | log_debug("new_pos = " << new_pos); |
203 | log_debug("history size = " << history.size()); |
204 | if ((new_pos > 0) && (new_pos <= history.size())){ |
205 | // position = new_rel_pos; |
206 | historyCall = true; |
207 | std::string url = history[new_pos-1].first; |
208 | log_debug("url is = " << url); |
209 | // const char* uri = url.c_str(); |
210 | // getArticleFromUrl(const_cast<char*>(url.c_str()), 1); |
211 | const char* uri = url.c_str(); |
212 | getArticleFromUrl(uri, new_rel_pos); |
213 | position = new_rel_pos; |
214 | }else{ |
215 | log_debug("jump not possible"); |
216 | window2->connect_all(); |
217 | } |
218 | } |
219 | |
220 | //UNUSED |
221 | // // copy article html from while loop into global variable |
222 | // void set_article(const std::string& txt) |
223 | // { |
224 | // content = txt; |
225 | // } |
226 | |
227 | // // // externally called functions |
228 | |
229 | |
230 | // // display random article |
231 | void show_random() |
232 | { |
233 | // window2->disconnect_all(); |
234 | |
235 | screenblock(1); |
236 | // log_debug("random called. window all disconnected"); |
237 | zim::File m = get_file(); |
238 | zim::Article article; |
239 | do |
240 | { |
241 | unsigned int seed = static_cast<unsigned int>(time(0)); |
242 | zim::size_type idx = static_cast<zim::size_type>(static_cast<double>(m.getCountArticles()) * rand_r(&seed) / RAND_MAX); |
243 | |
244 | article = m.getArticle(idx); |
245 | |
246 | //loop in case article is redirect |
247 | do |
248 | { |
249 | article = article.getRedirectArticle(); |
250 | }while(article.isRedirect()); |
251 | |
252 | }while(article.getUrl() == current_url); |
253 | |
254 | std::string res = article.getPage(); |
255 | |
256 | // log_debug("article size=" << res.size()); |
257 | // log_debug("article title=" << article.getTitle()); |
258 | // log_debug("article namespace=" << article.getNamespace()); |
259 | // position = 0; |
260 | fill_gtkhtml(res, article.getUrl(), article.getTitle()); |
261 | // log_debug("random called. window all connected"); |
262 | |
263 | } |
264 | |
265 | // // display search dialog |
266 | void search_window(main_window *window_x) |
267 | { |
268 | // html_color = html_color_new_from_rgb('255','0','0'); |
269 | // gtk_html_set_color(GTK_HTML(html_wg),html_color); |
270 | search_dialog(window_x, " "); |
271 | |
272 | } |
273 | |
274 | // // |
275 | void GoToTop() |
276 | { |
277 | log_debug("go to top called"); |
278 | std::string term = "top"; |
279 | bool success = gtk_html_jump_to_anchor(GTK_HTML(html_wg), const_cast<char*>(term.c_str())); |
280 | } |
281 | |
282 | // // // meta functions |
283 | |
284 | // // set displayed html to given url |
285 | // // FIXME: returns several articles and displays on one page ... why? |
286 | void getArticleFromUrl(const gchar *url, int pos) |
287 | { |
288 | // TODO unescape url |
289 | |
290 | // // create ZIM file accessor |
291 | zim::File m = get_file(); |
292 | // // convert url to string |
293 | std::string term; |
294 | term.assign(url); |
295 | // log_debug("str url" << term << "\n"); |
296 | std::string content; |
297 | // // replace '+' signs with spaces in url |
298 | std::replace(term.begin(), term.end(), '+', ' '); |
299 | |
300 | // // find and declare namespace |
301 | size_t found; |
302 | size_t found2; |
303 | char ns; |
304 | |
305 | found=term.find("/"); |
306 | if (found != std::string::npos) |
307 | { |
308 | if (found) |
309 | { |
310 | ns = term[0]; |
311 | term.erase(found + 1); |
312 | } |
313 | else |
314 | { |
315 | term.erase(1); |
316 | ns = term[0]; |
317 | found2 = term.find("/"); |
318 | term.erase(found2 + 1); |
319 | } |
320 | } |
321 | else |
322 | { |
323 | ns = 'A'; |
324 | } |
325 | |
326 | // // try to retrieve article |
327 | try |
328 | { |
329 | // #if HAVE_ZIM_QUNICODE_H |
330 | // zim::Article article = m.getArticle(ns, zim::QUnicodeString(term)); |
331 | // #else |
332 | zim::Article article = m.getArticle(ns, term); |
333 | // #endif |
334 | |
335 | if (article.good()) // check if article is really found |
336 | { |
337 | content = article.getPage(); |
338 | position = pos; |
339 | fill_gtkhtml(content, article.getUrl(), article.getTitle()); |
340 | } |
341 | else |
342 | { |
343 | std::cerr << "article \"" << url << "\" not found" << std::endl; |
344 | show_message("Error", "The article you requested (" + term + ") was not found."); |
345 | } |
346 | } |
347 | catch (const std::exception& e) |
348 | { |
349 | std::cerr << e.what() << std::endl; |
350 | } |
351 | } |
352 | |
353 | |
354 | // // test functions |
355 | void getArticleFromTitle(const gchar *phrase) |
356 | { |
357 | historyCall = false; |
358 | screenblock(1); |
359 | char ns; |
360 | ns = 'A'; |
361 | zim::File z = get_file(); |
362 | |
363 | zim::Search::Results result; |
364 | zim::Search search(z); |
365 | std::string term(phrase); |
366 | std::string res, url, title; |
367 | search.setSearchLimit(25); |
368 | search.find(result, ns, term); |
369 | if( result.size() == 0) |
370 | { |
371 | show_message("Error", "The article you requested (" + term + ") was not found."); |
372 | screenblock(0); |
373 | }else{ |
374 | |
375 | if (result.size() == 1){ |
376 | log_debug("one article in result"); |
377 | |
378 | zim::Article article = z.getArticle(result[0].getArticle().getIndex()); |
379 | |
380 | //loop in case article is redirect |
381 | do |
382 | { |
383 | article = article.getRedirectArticle(); |
384 | }while(article.isRedirect()); |
385 | |
386 | res = article.getPage(false, 10); |
387 | url = article.getUrl(); |
388 | title = article.getTitle(); |
389 | log_debug("set article from title position = 0"); |
390 | position = 0; |
391 | } |
392 | else |
393 | { |
394 | log_debug("more than one article in result"); |
395 | for (unsigned i = 0; i < result.size(); ++i) |
396 | { |
397 | // #if HAVE_ZIM_QUNICODE_H |
398 | // res += "<li><a href=" + result[i].getArticle().getLongUrl().toXML() + ">" + result[i].getArticle().getLongUrl().toXML() + "</a></li>"; |
399 | // #else |
400 | res += "<li><a href='" + result[i].getArticle().getUrl() + "'>" + result[i].getArticle().getUrl() + "</a></li>"; |
401 | // #endif |
402 | } |
403 | url = ""; |
404 | title = ""; |
405 | |
406 | } |
407 | |
408 | fill_gtkhtml(res, url, title); |
409 | } |
410 | } |
411 | |
412 | // // // window response functions |
413 | |
414 | // // open requested link |
415 | // FIXME show error window when dead-link requested |
416 | bool on_link_clicked(GtkHTML *html, const gchar *url) |
417 | { |
418 | std::string term(url); |
419 | size_t found; |
420 | found=term.find("#"); |
421 | if (found != std::string::npos){ |
422 | |
423 | if (found == 0){ |
424 | term.erase(0, 1); |
425 | bool success = gtk_html_jump_to_anchor(GTK_HTML(html_wg), const_cast<char*>(term.c_str())); |
426 | log_debug("jump to " << term << "worked: " << success); |
427 | }else{ |
428 | term = term.substr(0, found); |
429 | //TODO make "set_html" func accept anchor name |
430 | const char* uri = term.c_str(); |
431 | getArticleFromUrl(uri, position); |
432 | } |
433 | |
434 | }else{ |
435 | getArticleFromUrl(url,position); |
436 | } |
437 | |
438 | return true; |
439 | } |
440 | |
441 | bool scrolled(GtkHTML *cb_html, GtkOrientation orientation, GtkScrollType scroll_type, gfloat position) |
442 | { |
443 | log_debug("scrolled"); |
444 | // TODO on any scroll adjust cursor, maybe with: |
445 | //static void gtk_html_adjust_cursor_position (GtkHTML *html) |
446 | } |
447 | |
448 | // // main function |
449 | int main(int argc, char **argv) |
450 | { |
451 | try |
452 | { |
453 | log_init(); |
454 | |
455 | if (argc == 2) |
456 | { |
457 | fileName = argv[1]; |
458 | |
459 | log_debug("vido is here file: " << fileName); |
460 | |
461 | Gtk::Main kit(argc, argv); |
462 | |
463 | main_window window; |
464 | window.set_title("Vido"); |
465 | // window.set_border_width(0); |
466 | window.set_default_size(220, 240); |
467 | // window.set_resizable(0); |
468 | window.connect_all(); |
469 | html_wg = gtk_html_new(); |
470 | window2 = &window; |
471 | html = Glib::wrap(html_wg); |
472 | g_signal_connect( G_OBJECT( html_wg ), "link_clicked", G_CALLBACK( on_link_clicked ), NULL ); |
473 | g_signal_connect( G_OBJECT( html_wg ), "scroll", G_CALLBACK( scrolled ), NULL ); |
474 | gtk_html_set_caret_mode(GTK_HTML(html_wg),false); |
475 | // gtk_html_adjust_cursor_position(GTK_HTML(html_wg)); |
476 | Gtk::ScrolledWindow scrolled_window; |
477 | scrolled_window2 = &scrolled_window; |
478 | scrolled_window.add(*html); |
479 | scrolled_window.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); |
480 | |
481 | window.add(scrolled_window); |
482 | window.show_all(); |
483 | position = 0; |
484 | show_random(); |
485 | gtk_html_edit_make_cursor_visible(GTK_HTML(html_wg)); |
486 | Gtk::Main::run(window); |
487 | } |
488 | else |
489 | { |
490 | std::cout << "usage: " << argv[0] << " [PATH TO ZIM FILE]" << std::endl; |
491 | } |
492 | |
493 | } |
494 | catch (const std::exception& e) |
495 | { |
496 | std::cerr << e.what() << std::endl; |
497 | return -1; |
498 | } |
499 | |
500 | } |
501 | |
502 |
Branches:
development
master