Update to r2244:1076d8ddcf18
Axel Beckert
11 years ago
1 | 1 | Dillo project |
2 | 2 | ============================================================================= |
3 | 3 | |
4 | dillo-3.0 [not released yet] | |
4 | dillo-3.0 [September ??, 2011] | |
5 | 5 | |
6 | 6 | +- Ported Dillo to FLTK-1.3. |
7 | 7 | Patch: corvid, Johannes Hofmann, Jorge Arellano Cid |
8 | +- Default binding for close-all changed from Alt-q to Ctrl-q. | |
8 | +- Rewrote the User Interface: much simpler design and event handling. | |
9 | - Avoid double render after going Back or Forward (takes half the time now!). | |
10 | - Added on-the-fly panel resize (tiny/small/medium and normal/small icons). | |
11 | - Implemented a custom tabs handler (to allow fine control of it). | |
12 | - Rewrote dw's crossing-events dispatcher (avoids redundant events). | |
13 | - Fixed a years old bug: stamped tooltips when scrolling with keyboard. | |
14 | - Allow multiple search engines to be set in dillorc, with a menu in the web | |
15 | search dialog to select between them. | |
16 | - Added an optional label to dillorc's search_url. Format: "[<label> ]<url>" | |
17 | - Fixed a border case in URL resolver: empty path + {query|fragment} (BUG#948) | |
18 | - Avoid a certificate dialog storm on some HTTPS sites (BUG#868). | |
19 | - Cancel the expected URL after offering a download (BUG#982) | |
20 | - Default binding for close-all changed from Alt-q to Ctrl-q. | |
9 | 21 | - Default binding for close-tab changed from Ctrl-q to Ctrl-w. |
10 | 22 | - Add right_click_closes_tab preference (default is middle click). |
11 | 23 | - 'hide-panels' key action now hides the findbar if present, and toggles |
12 | 24 | display of the control panels otherwise. |
25 | - Removed 'large' option of panel_size preference. | |
13 | 26 | - Remove 'fullscreen' key action. |
27 | - Eliminated a pack of 22 compiler warnings (gcc-4.6.1 amd64) | |
28 | - Lots of minor bug-fixes. | |
14 | 29 | Patches: Jorge Arellano Cid |
15 | 30 | +- Remove --enable-ansi configure option. |
16 | 31 | - Limit saved cookie size. |
17 | 32 | - Allow binding to non-ASCII keys and multimedia keys. |
18 | 33 | - Enable line wrapping for <textarea>. (BUG#903) |
34 | - Wrap image alt text. | |
19 | 35 | Patches: corvid |
36 | +- Add support for CSS adjacent sibling selectors. | |
37 | - Collapse parent's and first child's top margin. | |
38 | Patch: Johannes Hofmann | |
39 | +- Default binding for left-tab changed to Shift-Ctrl-Tab. | |
40 | Patch: Jeremy Henty | |
41 | ||
42 | Note: these are user-visible changes. The full Changelog is kept | |
43 | in our Mercurial repository at http://hg.dillo.org/dillo | |
44 | ||
20 | 45 | |
21 | 46 | ----------------------------------------------------------------------------- |
22 | 47 | |
23 | dillo-2.2.1 [not released yet] | |
24 | ||
25 | +- Implemented "View source" as a dpi. | |
48 | dillo-2.2.1 [July 18, 2011] | |
49 | ||
50 | +- Fix fullwindow start. | |
51 | - Implemented "View source" as a dpi. | |
52 | - Fix: vsource html, fix entities display, indentation. | |
26 | 53 | - Accept application/xhtml+xml. |
27 | 54 | - Small caps support. |
28 | 55 | - Border-collapse, border-style properties. |
56 | - Removed gcc warnings for 64bit | |
29 | 57 | Patches: Jorge Arellano Cid |
30 | 58 | +- Configurable User-Agent HTTP header. |
31 | 59 | Patch: Alexander Voigt, corvid |
26 | 26 | |
27 | 27 | The new FLTK2-based dillo code is released! (under GPL3) |
28 | 28 | |
29 | Jul 2011 | |
30 | ||
31 | Dillo switches to fltk-1.3.0. | |
32 | dillo-2.2.1, the last of the 2.x series is released. | |
33 | ||
34 | Sep 2011 | |
35 | ||
36 | dillo-3.0, the first of the 3.x series is released. | |
37 | ||
38 | ||
29 | 39 | Jorge.- |
30 | 40 | jcid@dillo.org |
31 | 41 | Project maintainer, core developer, patcher, you name it! :-) |
1 | 1 | Dillo web browser |
2 | 2 | =================== |
3 | 3 | |
4 | Dillo 2.2 features a major overhaul of the cookies subsystem, | |
5 | a reimplementation of the Dillo Plugin (DPI) API, a configurable | |
6 | connection limit, and various CSS improvements. | |
4 | Dillo-3.0 is the first release of the newest branch, which is | |
5 | based on the already released fltk-1.3 toolkit. This is big news | |
6 | because it clears the way for Dillo to return to those | |
7 | distributions which had excluded Dillo2 due to FLTK2 never | |
8 | being officially released. | |
7 | 9 | |
8 | This release is part of the Dillo2 series. In Dillo2, significant | |
9 | parts of the codebase were ported to C++, and the rendering engine was | |
10 | modified to use the FLTK2 library rather than GTK1. | |
10 | The development effort shifted to the dillo-3.x branch long ago, | |
11 | and it became the active one; dillo-2.x received less and less | |
12 | attention, and dillo-2.2.1 became the last of its series. | |
11 | 13 | |
12 | Here's a list of some well-known problems: | |
14 | The core team will focus on implementing the CSS feature of | |
15 | floating elements. This will greatly improve dillo's web page | |
16 | rendering since many sites have adopted floats instead of tables. | |
17 | ||
18 | The new dillo3 has shown excellent stability. | |
19 | ||
20 | The core team welcomes developers willing to join our workforce. | |
21 | ||
22 | ||
23 | Here's a list of some old well-known problems of dillo: | |
13 | 24 | |
14 | 25 | * no FRAMES rendering |
15 | 26 | * no https (there's a barebones prototype). |
16 | 27 | |
17 | ----- | |
18 | FLTK2 | |
19 | ----- | |
20 | 28 | |
21 | The FLTK2 library is statically linked into Dillo2. | |
22 | You can get it from fltk.org. | |
23 | The recommended version is >= r6916. e.g. in: | |
29 | ------- | |
30 | FLTK1.3 | |
31 | ------- | |
24 | 32 | |
25 | http://fltk.org/software.php?VERSION=2.0.x-r6916 | |
33 | You can get FLTK-1.3 here: | |
34 | ||
35 | http://fltk.org/software.php?VERSION=1.3.0 | |
36 | ||
37 | ||
38 | --------------------------- | |
39 | Building dillo from sources | |
40 | --------------------------- | |
41 | ||
42 | 1.- Install fltk: | |
43 | ||
44 | tar jxvf fltk-1.3.0-source.tar.gz | |
45 | cd fltk-1.3.0 | |
46 | less README.unix | |
47 | make | |
48 | sudo make install | |
49 | cd .. | |
50 | ||
51 | (don't configure fltk with --enable-cairo) | |
52 | ||
53 | 2.- Then dillo3: | |
54 | ||
55 | tar jxvf dillo-3.0.tar.bz2 | |
56 | cd dillo-3.0 | |
57 | ./autogen.sh; ./configure; make | |
58 | sudo make install-strip | |
26 | 59 | |
27 | 60 | |
28 | 61 | ------------ |
66 | 99 | |
67 | 100 | Jorge.- |
68 | 101 | (jcid@dillo.org) |
69 | June, 2009 | |
102 | August, 2011 |
77 | 77 | /* Define to the one symbol short name of this package. */ |
78 | 78 | #undef PACKAGE_TARNAME |
79 | 79 | |
80 | /* Define to the home page for this package. */ | |
81 | #undef PACKAGE_URL | |
82 | ||
80 | 83 | /* Define to the version of this package. */ |
81 | 84 | #undef PACKAGE_VERSION |
82 | 85 | |
102 | 105 | #undef VERSION |
103 | 106 | |
104 | 107 | /* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>, |
105 | <pthread.h>, or <semaphore.h> is not used. If the typedef was allowed, the | |
108 | <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the | |
106 | 109 | #define below would cause a syntax error. */ |
107 | 110 | #undef _UINT32_T |
108 | 111 |
111 | 111 | # Set the URLs used by the web search dialog. |
112 | 112 | # "%s" is replaced with the search keywords separated by '+'. |
113 | 113 | # Format: search_url="[<label> ]<url>" |
114 | # You can have several search engines, with the first being the default. | |
115 | # e.g. | |
116 | # search_url="DuckDuckGo http://duckduckgo.com/html?q=%s" | |
117 | # search_url="Wikipedia http://www.wikipedia.org/wiki/Special:Search?search=%s" | |
118 | # search_url="Lycos http://search.lycos.com/?query=%s" | |
119 | #search_url="http://www.google.com/search?ie=UTF-8&oe=UTF-8&q=%s" | |
114 | # You can enable multiple search_url strings at once and select from among | |
115 | # them at runtime, with the first being the default. | |
116 | search_url="http://duckduckgo.com/lite/?kp=-1&q=%s" | |
117 | search_url="Scroogle https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s" | |
118 | search_url="Wikipedia http://www.wikipedia.org/w/index.php?search=%s&go=Go" | |
119 | search_url="Free Dictionary http://www.thefreedictionary.com/%s" | |
120 | search_url="Google http://www.google.com/search?ie=UTF-8&oe=UTF-8&q=%s" | |
120 | 121 | |
121 | 122 | # If set, dillo will ask web servers to send pages in this language. |
122 | 123 | # This setting does NOT change dillo's user interface. |
190 | 191 | # USER INTERFACE SECTION |
191 | 192 | #------------------------------------------------------------------------- |
192 | 193 | |
193 | # Size of dillo panel (used to enlarge the browsing area) | |
194 | # tiny : recommended for iPAQ (with small_icons) | |
195 | # small : very nice! (it's "medium" without icon titles) | |
196 | # medium : nice! | |
197 | # large : Traditional | |
194 | # Size of dillo panel | |
195 | # tiny : buttons, location, and progress boxes in one row | |
196 | # small : location in one row, buttons + progress boxes in another | |
197 | # medium : adds text labels to buttons and boxes | |
198 | 198 | # panel_size=tiny |
199 | 199 | # panel_size=small |
200 | 200 | #panel_size=medium |
201 | # panel_size=large | |
202 | 201 | |
203 | 202 | #small_icons=NO |
204 | 203 |
876 | 876 | |
877 | 877 | dstr = dStr_sized_new(64); |
878 | 878 | while ((ch = fgetc(stream)) != EOF) { |
879 | if (ch == '\\') { | |
880 | /* continue with the next line */ | |
881 | while ((ch = fgetc(stream)) != EOF && ch != '\n') ; | |
882 | continue; | |
883 | } | |
884 | 879 | dStr_append_c(dstr, ch); |
885 | 880 | if (ch == '\n') |
886 | 881 | break; |
308 | 308 | <tr><td>Shift-Back or "<b>.</b>" <td>> <td>next page |
309 | 309 | <tr><td>Alt-F <td>File <td>file menu |
310 | 310 | <tr><td>Ctrl-TabKey <td>TabKey <td>Next tab |
311 | <tr><td>Shift-TabKey <td>TabKey <td>Previous tab | |
311 | <tr><td>Ctrl-Shift-TabKey <td>TabKey <td>Previous tab | |
312 | 312 | <tr><td>Esc <td>escape <td>close dialog, |
313 | 313 | close findbar,<br> |
314 | 314 | Hide/show control panels |
107 | 107 | int LogPipe[2]; |
108 | 108 | char *shortname, *fullname; |
109 | 109 | char *target_dir; |
110 | int log_len, log_max, log_state; | |
110 | size_t log_len, log_max; | |
111 | int log_state; | |
111 | 112 | char *log_text; |
112 | 113 | time_t init_time; |
113 | 114 | char **dl_argv; |
189 | 190 | DLAction check_filename(char **p_dl_dest); |
190 | 191 | }; |
191 | 192 | |
193 | /* | |
194 | * FLTK cannot be dissuaded from interpreting '@' in a tooltip | |
195 | * as indicating a symbol unless we escape it. | |
196 | */ | |
197 | static char *escape_tooltip(const char *buf, ssize_t len) | |
198 | { | |
199 | if (len < 0) | |
200 | len = 0; | |
201 | ||
202 | char *ret = (char *) malloc(2 * len + 1); | |
203 | char *dest = ret; | |
204 | ||
205 | while (len-- > 0) { | |
206 | if (*buf == '@') | |
207 | *dest++ = *buf; | |
208 | *dest++ = *buf++; | |
209 | } | |
210 | *dest = '\0'; | |
211 | ||
212 | return ret; | |
213 | } | |
214 | ||
192 | 215 | |
193 | 216 | /* |
194 | 217 | * Global variables |
475 | 498 | void DLItem::log_text_add(const char *buf, ssize_t st) |
476 | 499 | { |
477 | 500 | const char *p; |
478 | char *q, *d, num[64]; | |
501 | char *esc_str, *q, *d, num[64]; | |
502 | size_t esc_len; | |
503 | ||
504 | // WORKAROUND: We have to escape '@' in FLTK tooltips. | |
505 | esc_str = escape_tooltip(buf, st); | |
506 | esc_len = strlen(esc_str); | |
479 | 507 | |
480 | 508 | // Make room... |
481 | if (log_len + st >= log_max) { | |
482 | log_max = log_len + st + 1024; | |
509 | if (log_len + esc_len >= log_max) { | |
510 | log_max = log_len + esc_len + 1024; | |
483 | 511 | log_text = (char *) realloc (log_text, log_max); |
484 | 512 | log_text[log_len] = 0; |
485 | 513 | prTitle->tooltip(log_text); |
487 | 515 | |
488 | 516 | // FSM to remove wget's "dot-progress" (i.e. "^ " || "^[0-9]+K") |
489 | 517 | q = log_text + log_len; |
490 | for (p = buf; (p - buf) < st; ++p) { | |
518 | for (p = esc_str; (size_t)(p - esc_str) < esc_len; ++p) { | |
491 | 519 | switch (log_state) { |
492 | 520 | case ST_newline: |
493 | 521 | if (*p == ' ') { |
522 | 550 | *q = 0; |
523 | 551 | log_len = strlen(log_text); |
524 | 552 | |
553 | free(esc_str); | |
554 | ||
525 | 555 | // Now scan for the length of the file |
526 | 556 | if (total_bytesize == -1) { |
527 | 557 | p = strstr(log_text, "\nLength: "); |
1091 | 1121 | |
1092 | 1122 | // --------------------------------------------------------------------------- |
1093 | 1123 | |
1124 | /* | |
1125 | * Set FL_NORMAL_LABEL to interpret neither symbols (@) nor shortcuts (&) | |
1126 | */ | |
1127 | static void custLabelDraw(const Fl_Label* o, int X, int Y, int W, int H, | |
1128 | Fl_Align align) | |
1129 | { | |
1130 | const int interpret_symbols = 0; | |
1131 | ||
1132 | fl_draw_shortcut = 0; | |
1133 | fl_font(o->font, o->size); | |
1134 | fl_color((Fl_Color)o->color); | |
1135 | fl_draw(o->value, X, Y, W, H, align, o->image, interpret_symbols); | |
1136 | } | |
1137 | ||
1138 | static void custLabelMeasure(const Fl_Label* o, int& W, int& H) | |
1139 | { | |
1140 | const int interpret_symbols = 0; | |
1141 | ||
1142 | fl_draw_shortcut = 0; | |
1143 | fl_font(o->font, o->size); | |
1144 | fl_measure(o->value, W, H, interpret_symbols); | |
1145 | } | |
1146 | ||
1094 | 1147 | |
1095 | 1148 | |
1096 | 1149 | //int main(int argc, char **argv) |
1100 | 1153 | |
1101 | 1154 | Fl::lock(); |
1102 | 1155 | |
1156 | // Disable '@' and '&' interpretation in normal labels. | |
1157 | Fl::set_labeltype(FL_NORMAL_LABEL, custLabelDraw, custLabelMeasure); | |
1158 | ||
1103 | 1159 | // Create the download window |
1104 | 1160 | dl_win = new DLWin(ww, wh); |
1105 | 1161 |
129 | 129 | SSL * ssl_connection = NULL; |
130 | 130 | |
131 | 131 | char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *http_query = NULL, |
132 | *proxy_url = NULL, *proxy_connect = NULL; | |
132 | *proxy_url = NULL, *proxy_connect = NULL, *check_cert = NULL; | |
133 | 133 | char buf[4096]; |
134 | 134 | int ret = 0; |
135 | 135 | int network_socket = -1; |
199 | 199 | a_Dpip_get_attr(dpip_tag, "proxy_connect"); |
200 | 200 | url = a_Dpip_get_attr(dpip_tag, "url"); |
201 | 201 | http_query = a_Dpip_get_attr(dpip_tag, "query"); |
202 | ||
203 | if (cmd == NULL || url == NULL || http_query == NULL){ | |
202 | if (!(check_cert = a_Dpip_get_attr(dpip_tag, "check_cert"))) { | |
203 | /* allow older dillo versions use this dpi */ | |
204 | check_cert = dStrdup("true"); | |
205 | } | |
206 | ||
207 | if (!cmd || !url || !http_query) { | |
204 | 208 | MSG("***Value of cmd, url or http_query is NULL" |
205 | 209 | " - cannot continue\n"); |
206 | 210 | exit_error = 1; |
287 | 291 | |
288 | 292 | /*Use handle error function to decide what to do*/ |
289 | 293 | if (exit_error == 0){ |
290 | if (handle_certificate_problem(ssl_connection) < 0){ | |
294 | if (strcmp(check_cert, "true") == 0 && | |
295 | handle_certificate_problem(ssl_connection) < 0){ | |
291 | 296 | MSG("Certificate verification error\n"); |
292 | 297 | exit_error = 1; |
293 | 298 | } |
321 | 326 | dFree(http_query); |
322 | 327 | dFree(proxy_url); |
323 | 328 | dFree(proxy_connect); |
329 | dFree(check_cert); | |
324 | 330 | |
325 | 331 | if (network_socket != -1){ |
326 | 332 | close(network_socket); |
145 | 145 | fl_draw(text, len, translateCanvasXToViewX (x), translateCanvasYToViewY(y)); |
146 | 146 | } |
147 | 147 | |
148 | void FltkPreview::drawSimpleWrappedText (core::style::Font *font, | |
149 | core::style::Color *color, | |
150 | core::style::Color::Shading shading, | |
151 | int x, int y, int w, int h, | |
152 | const char *text) | |
153 | { | |
154 | } | |
155 | ||
148 | 156 | void FltkPreview::drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot, |
149 | 157 | int x, int y, int width, int height) |
150 | 158 | { |
41 | 41 | core::style::Color *color, |
42 | 42 | core::style::Color::Shading shading, |
43 | 43 | int x, int y, const char *text, int len); |
44 | void drawSimpleWrappedText (core::style::Font *font, | |
45 | core::style::Color *color, | |
46 | core::style::Color::Shading shading, | |
47 | int x, int y, int w, int h, | |
48 | const char *text); | |
44 | 49 | void drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot, |
45 | 50 | int x, int y, int width, int height); |
46 | 51 |
354 | 354 | |
355 | 355 | void FltkLabelButtonResource::setLabel (const char *label) |
356 | 356 | { |
357 | delete this->label; | |
357 | free((char *)this->label); | |
358 | 358 | this->label = strdup (label); |
359 | 359 | |
360 | 360 | widget->label (this->label); |
604 | 604 | FltkSpecificResource <dw::core::ui::MultiLineTextResource> (platform) |
605 | 605 | { |
606 | 606 | buffer = new Fl_Text_Buffer; |
607 | text_copy = NULL; | |
607 | 608 | editable = false; |
608 | 609 | |
609 | 610 | numCols = cols; |
627 | 628 | /* Free memory avoiding a double-free of text buffers */ |
628 | 629 | ((Fl_Text_Editor *) widget)->buffer (0); |
629 | 630 | delete buffer; |
631 | if (text_copy) | |
632 | free(text_copy); | |
630 | 633 | } |
631 | 634 | |
632 | 635 | Fl_Widget *FltkMultiLineTextResource::createNewWidget (core::Allocation |
677 | 680 | |
678 | 681 | const char *FltkMultiLineTextResource::getText () |
679 | 682 | { |
680 | return buffer->text (); | |
683 | /* FLTK-1.3 insists upon returning a new copy of the buffer text, so | |
684 | * we have to keep track of it. | |
685 | */ | |
686 | if (text_copy) | |
687 | free(text_copy); | |
688 | text_copy = buffer->text(); | |
689 | return text_copy; | |
681 | 690 | } |
682 | 691 | |
683 | 692 | void FltkMultiLineTextResource::setText (const char *text) |
315 | 315 | { |
316 | 316 | private: |
317 | 317 | Fl_Text_Buffer *buffer; |
318 | char *text_copy; | |
318 | 319 | bool editable; |
319 | 320 | int numCols, numRows; |
320 | 321 |
470 | 470 | int x = translateCanvasXToViewX (centerX) - width / 2; |
471 | 471 | int y = translateCanvasYToViewY (centerY) - height / 2; |
472 | 472 | |
473 | fl_arc(x, y, width, height, 0.0, 360.0); | |
473 | fl_arc(x, y, width, height, angle1, angle2); | |
474 | 474 | if (filled) |
475 | fl_pie(x, y, width, height, 0.0, 360.0); | |
475 | fl_pie(x, y, width, height, angle1, angle2); | |
476 | 476 | } |
477 | 477 | |
478 | 478 | void FltkViewBase::drawPolygon (core::style::Color *color, |
479 | 479 | core::style::Color::Shading shading, |
480 | bool filled, bool convex, int points[][2], | |
480 | bool filled, bool convex, core::Point *points, | |
481 | 481 | int npoints) |
482 | 482 | { |
483 | 483 | if (npoints > 0) { |
492 | 492 | fl_begin_loop(); |
493 | 493 | |
494 | 494 | for (int i = 0; i < npoints; i++) { |
495 | fl_vertex(translateCanvasXToViewX(points[i][0]), | |
496 | translateCanvasYToViewY(points[i][1])); | |
495 | fl_vertex(translateCanvasXToViewX(points[i].x), | |
496 | translateCanvasYToViewY(points[i].y)); | |
497 | 497 | } |
498 | 498 | if (filled) { |
499 | 499 | if (convex) |
581 | 581 | } |
582 | 582 | } |
583 | 583 | |
584 | /* | |
585 | * "simple" in that it ignores letter-spacing, etc. This was added for image | |
586 | * alt text where none of that matters. | |
587 | */ | |
588 | void FltkWidgetView::drawSimpleWrappedText (core::style::Font *font, | |
589 | core::style::Color *color, | |
590 | core::style::Color::Shading shading, | |
591 | int X, int Y, int W, int H, | |
592 | const char *text) | |
593 | { | |
594 | FltkFont *ff = (FltkFont*)font; | |
595 | fl_font(ff->font, ff->size); | |
596 | fl_color(((FltkColor*)color)->colors[shading]); | |
597 | fl_draw(text, | |
598 | translateCanvasXToViewX (X), translateCanvasYToViewY (Y), | |
599 | W, H, FL_ALIGN_TOP|FL_ALIGN_LEFT|FL_ALIGN_WRAP, NULL, 0); | |
600 | } | |
601 | ||
584 | 602 | void FltkWidgetView::drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot, |
585 | 603 | int X, int Y, int width, int height) |
586 | 604 | { |
97 | 97 | int angle1, int angle2); |
98 | 98 | void drawPolygon (core::style::Color *color, |
99 | 99 | core::style::Color::Shading shading, |
100 | bool filled, bool convex, int points[][2], int npoints); | |
100 | bool filled, bool convex, | |
101 | core::Point *points, int npoints); | |
101 | 102 | |
102 | 103 | core::View *getClippingView (int x, int y, int width, int height); |
103 | 104 | void mergeClippingView (core::View *clippingView); |
115 | 116 | core::style::Color *color, |
116 | 117 | core::style::Color::Shading shading, |
117 | 118 | int x, int y, const char *text, int len); |
119 | void drawSimpleWrappedText (core::style::Font *font, | |
120 | core::style::Color *color, | |
121 | core::style::Color::Shading shading, | |
122 | int x, int y, int w, int h, | |
123 | const char *text); | |
118 | 124 | void drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot, |
119 | 125 | int x, int y, int width, int height); |
120 | 126 |
342 | 342 | |
343 | 343 | /* |
344 | 344 | * For scrollbars, this currently sets the same step to both vertical and |
345 | * horizontal. It may me differentiated if necessary. | |
345 | * horizontal. It may be differentiated if necessary. | |
346 | 346 | */ |
347 | 347 | void FltkViewport::setScrollStep(int step) |
348 | 348 | { |
383 | 383 | getContentHeight()); |
384 | 384 | } |
385 | 385 | |
386 | usedView->drawText (getStyle()->font, getStyle()->color, | |
386 | usedView->drawSimpleWrappedText (getStyle()->font, getStyle()->color, | |
387 | 387 | core::style::Color::SHADING_NORMAL, |
388 | 388 | allocation.x + getStyle()->boxOffsetX (), |
389 | allocation.y + getStyle()->boxOffsetY () | |
390 | + getStyle()->font->ascent, | |
391 | altText, strlen(altText)); | |
389 | allocation.y + getStyle()->boxOffsetY (), | |
390 | getContentWidth(), getContentHeight(), altText); | |
392 | 391 | |
393 | 392 | if (clippingView) |
394 | 393 | view->mergeClippingView (clippingView); |
193 | 193 | canvasWidth = canvasAscent = canvasDescent = 0; |
194 | 194 | |
195 | 195 | usesViewport = false; |
196 | drawAfterScrollReq = false; | |
196 | 197 | scrollX = scrollY = 0; |
197 | 198 | viewportWidth = viewportHeight = 0; |
198 | 199 | hScrollbarThickness = vScrollbarThickness = 0; |
455 | 456 | if (xChanged || yChanged) { |
456 | 457 | adjustScrollPos (); |
457 | 458 | view->scrollTo (scrollX, scrollY); |
459 | if (drawAfterScrollReq) { | |
460 | drawAfterScrollReq = false; | |
461 | view->queueDrawTotal (); | |
462 | } | |
458 | 463 | } |
459 | 464 | |
460 | 465 | scrollIdleId = -1; |
502 | 507 | { |
503 | 508 | Rectangle widgetArea, intersection, widgetDrawArea; |
504 | 509 | |
505 | if (topLevel) { | |
510 | if (scrollIdleId != -1) { | |
511 | /* scroll is pending, defer draw until after scrollIdle() */ | |
512 | drawAfterScrollReq = true; | |
513 | ||
514 | } else if (topLevel) { | |
506 | 515 | /* Draw the top level widget. */ |
507 | 516 | widgetArea.x = topLevel->allocation.x; |
508 | 517 | widgetArea.y = topLevel->allocation.y; |
813 | 822 | */ |
814 | 823 | void Layout::leaveNotify (View *view, ButtonState state) |
815 | 824 | { |
825 | #if 0 | |
816 | 826 | Widget *lastWidget; |
817 | 827 | EventCrossing event; |
818 | 828 | |
825 | 835 | event.currentWidget = widgetAtPoint; |
826 | 836 | lastWidget->leaveNotify (&event); |
827 | 837 | } |
838 | #else | |
839 | moveOutOfView (state); | |
840 | #endif | |
828 | 841 | } |
829 | 842 | |
830 | 843 | /* |
843 | 856 | |
844 | 857 | /* |
845 | 858 | * Emit the necessary crossing events, when the mouse pointer has moved to |
846 | * the given widget. | |
859 | * the given widget (by mouse or scrolling). | |
847 | 860 | */ |
848 | 861 | void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state) |
849 | 862 | { |
889 | 902 | for (w = newWidgetAtPoint; w != ancestor; w = w->getParent ()) |
890 | 903 | track[i--] = w; |
891 | 904 | } |
905 | #if 0 | |
906 | MSG("Track: %s[ ", widgetAtPoint ? "" : "nil "); | |
907 | for (i = 0; i < trackLen; i++) | |
908 | MSG("%s%p ", i == i_a ? ">" : "", track[i]); | |
909 | MSG("] %s\n", newWidgetAtPoint ? "" : "nil"); | |
910 | #endif | |
892 | 911 | |
893 | 912 | /* Send events to the widgets on the track */ |
894 | 913 | for (i = 0; i < trackLen; i++) { |
898 | 917 | if (i < i_a) { |
899 | 918 | track[i]->leaveNotify (&crossingEvent); |
900 | 919 | } else if (i == i_a) { /* ancestor */ |
901 | if (!widgetAtPoint) | |
920 | /* Don't touch ancestor unless: | |
921 | * - moving into/from NULL, | |
922 | * - ancestor becomes the newWidgetAtPoint */ | |
923 | if (i_a == trackLen-1 && !newWidgetAtPoint) | |
924 | track[i]->leaveNotify (&crossingEvent); | |
925 | else if ((i_a == 0 && !widgetAtPoint) || | |
926 | (i_a == trackLen-1 && newWidgetAtPoint)) | |
902 | 927 | track[i]->enterNotify (&crossingEvent); |
903 | else if (!newWidgetAtPoint) | |
904 | track[i]->leaveNotify (&crossingEvent); | |
905 | 928 | } else { |
906 | 929 | track[i]->enterNotify (&crossingEvent); |
907 | 930 | } |
137 | 137 | style::Cursor cursor; |
138 | 138 | int canvasWidth, canvasAscent, canvasDescent; |
139 | 139 | |
140 | bool usesViewport; | |
140 | bool usesViewport, drawAfterScrollReq; | |
141 | 141 | int scrollX, scrollY, viewportWidth, viewportHeight; |
142 | 142 | bool canvasHeightGreater; |
143 | 143 | int hScrollbarThickness, vScrollbarThickness; |
422 | 422 | int x1, int y1, int x2, int y2) |
423 | 423 | |
424 | 424 | { |
425 | int points[4][2], d, w; | |
425 | int d, w; | |
426 | Point points[4]; | |
426 | 427 | const bool filled = true, convex = true; |
427 | 428 | bool ridge = false, inset = false, dotted = false; |
428 | 429 | Color::Shading shading = Color::SHADING_NORMAL; |
452 | 453 | if (style->borderWidth.top == 1) { |
453 | 454 | view->drawLine(style->borderColor.top, shading, x1, y1, x2, y2); |
454 | 455 | } else { |
455 | points[0][0] = x1; | |
456 | points[1][0] = x2 + 1; | |
457 | points[0][1] = points[1][1] = y1; | |
458 | points[2][0] = points[1][0] - style->borderWidth.right; | |
459 | points[3][0] = x1 + style->borderWidth.left; | |
460 | points[2][1] = points[3][1] = points[0][1] + style->borderWidth.top; | |
456 | points[0].x = x1; | |
457 | points[1].x = x2 + 1; | |
458 | points[0].y = points[1].y = y1; | |
459 | points[2].x = points[1].x - style->borderWidth.right; | |
460 | points[3].x = x1 + style->borderWidth.left; | |
461 | points[2].y = points[3].y = points[0].y + style->borderWidth.top; | |
461 | 462 | view->drawPolygon (style->borderColor.top, shading, filled, convex, |
462 | 463 | points, 4); |
463 | 464 | } |
466 | 467 | ridge = true; |
467 | 468 | case BORDER_GROOVE: |
468 | 469 | d = style->borderWidth.top & 1; |
469 | points[0][0] = x1; | |
470 | points[1][0] = x2 + 1; | |
471 | points[0][1] = points[1][1] = y1; | |
472 | points[2][0] = x2 - style->borderWidth.right / 2; | |
473 | points[3][0] = x1 + style->borderWidth.left / 2; | |
474 | points[2][1] = points[3][1] = y1 + style->borderWidth.top / 2 + d; | |
470 | points[0].x = x1; | |
471 | points[1].x = x2 + 1; | |
472 | points[0].y = points[1].y = y1; | |
473 | points[2].x = x2 - style->borderWidth.right / 2; | |
474 | points[3].x = x1 + style->borderWidth.left / 2; | |
475 | points[2].y = points[3].y = y1 + style->borderWidth.top / 2 + d; | |
475 | 476 | shading = (ridge) ? Color::SHADING_LIGHT : Color::SHADING_DARK; |
476 | 477 | view->drawPolygon (style->borderColor.top, shading, filled, convex, |
477 | 478 | points, 4); |
478 | points[0][0] = x1 + style->borderWidth.left / 2 + d; | |
479 | points[1][0] = x2 - style->borderWidth.right / 2 + 1 - d; | |
480 | points[0][1] = points[1][1] = y1 + style->borderWidth.top / 2 + d; | |
481 | points[2][0] = x2 - style->borderWidth.right + 1 - d; | |
482 | points[3][0] = x1 + style->borderWidth.left; | |
483 | points[2][1] = points[3][1] = y1 + style->borderWidth.top; | |
479 | points[0].x = x1 + style->borderWidth.left / 2 + d; | |
480 | points[1].x = x2 - style->borderWidth.right / 2 + 1 - d; | |
481 | points[0].y = points[1].y = y1 + style->borderWidth.top / 2 + d; | |
482 | points[2].x = x2 - style->borderWidth.right + 1 - d; | |
483 | points[3].x = x1 + style->borderWidth.left; | |
484 | points[2].y = points[3].y = y1 + style->borderWidth.top; | |
484 | 485 | shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT; |
485 | 486 | view->drawPolygon (style->borderColor.top, shading, filled, convex, |
486 | 487 | points, 4); |
494 | 495 | view->drawLine(style->borderColor.top, shading, x1, y1, x2, y2); |
495 | 496 | break; |
496 | 497 | } |
497 | points[0][0] = x1; | |
498 | points[1][0] = x2 + 1; | |
499 | points[0][1] = points[1][1] = y1; | |
500 | points[2][0] = points[1][0] - w_r; | |
501 | points[3][0] = points[0][0] + w_l; | |
502 | points[2][1] = points[3][1] = points[0][1] + w; | |
498 | points[0].x = x1; | |
499 | points[1].x = x2 + 1; | |
500 | points[0].y = points[1].y = y1; | |
501 | points[2].x = points[1].x - w_r; | |
502 | points[3].x = points[0].x + w_l; | |
503 | points[2].y = points[3].y = points[0].y + w; | |
503 | 504 | view->drawPolygon (style->borderColor.top, shading, filled, convex, |
504 | 505 | points, 4); |
505 | points[0][0] = x1 + style->borderWidth.left - w_l; | |
506 | points[1][0] = x2 + 1 - style->borderWidth.right + w_r; | |
507 | points[0][1] = points[1][1] = y1 + w + d; | |
508 | points[2][0] = x2 + 1 - style->borderWidth.right; | |
509 | points[3][0] = x1 + style->borderWidth.left; | |
510 | points[2][1] = points[3][1] = y1 + style->borderWidth.top; | |
506 | points[0].x = x1 + style->borderWidth.left - w_l; | |
507 | points[1].x = x2 + 1 - style->borderWidth.right + w_r; | |
508 | points[0].y = points[1].y = y1 + w + d; | |
509 | points[2].x = x2 + 1 - style->borderWidth.right; | |
510 | points[3].x = x1 + style->borderWidth.left; | |
511 | points[2].y = points[3].y = y1 + style->borderWidth.top; | |
511 | 512 | view->drawPolygon (style->borderColor.top, shading, filled, convex, |
512 | 513 | points, 4); |
513 | 514 | break; |
518 | 519 | int x1, int y1, int x2, int y2) |
519 | 520 | |
520 | 521 | { |
521 | int points[4][2], d, w; | |
522 | int d, w; | |
523 | Point points[4]; | |
522 | 524 | const bool filled = true, convex = true; |
523 | 525 | bool ridge = false, inset = false, dotted = false; |
524 | 526 | Color::Shading shading = Color::SHADING_NORMAL; |
548 | 550 | if (style->borderWidth.bottom == 1) { /* 1 pixel line */ |
549 | 551 | view->drawLine(style->borderColor.bottom, shading, x1, y1, x2, y2); |
550 | 552 | } else { |
551 | points[0][0] = x1 - 1; | |
552 | points[1][0] = x2 + 2; | |
553 | points[0][1] = points[1][1] = y1 + 1; | |
554 | points[2][0] = points[1][0] - style->borderWidth.right; | |
555 | points[3][0] = points[0][0] + style->borderWidth.left; | |
556 | points[2][1] = points[3][1] = points[0][1]-style->borderWidth.bottom; | |
553 | points[0].x = x1 - 1; | |
554 | points[1].x = x2 + 2; | |
555 | points[0].y = points[1].y = y1 + 1; | |
556 | points[2].x = points[1].x - style->borderWidth.right; | |
557 | points[3].x = points[0].x + style->borderWidth.left; | |
558 | points[2].y = points[3].y = points[0].y-style->borderWidth.bottom; | |
557 | 559 | view->drawPolygon (style->borderColor.bottom, shading, filled, convex, |
558 | 560 | points, 4); |
559 | 561 | } |
563 | 565 | case BORDER_GROOVE: |
564 | 566 | w = style->borderWidth.bottom; |
565 | 567 | d = w & 1; |
566 | points[0][0] = x1 - 1; | |
567 | points[1][0] = x2 + 2 - d; | |
568 | points[0][1] = points[1][1] = y1 + 1; | |
569 | points[2][0] = points[1][0] - style->borderWidth.right / 2; | |
570 | points[3][0] = points[0][0] + style->borderWidth.left / 2 + d; | |
571 | points[2][1] = points[3][1] = points[0][1] - w/2 - d; | |
568 | points[0].x = x1 - 1; | |
569 | points[1].x = x2 + 2 - d; | |
570 | points[0].y = points[1].y = y1 + 1; | |
571 | points[2].x = points[1].x - style->borderWidth.right / 2; | |
572 | points[3].x = points[0].x + style->borderWidth.left / 2 + d; | |
573 | points[2].y = points[3].y = points[0].y - w/2 - d; | |
572 | 574 | shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT; |
573 | 575 | view->drawPolygon (style->borderColor.bottom, shading, filled, convex, |
574 | 576 | points, 4); |
575 | 577 | // clockwise |
576 | points[0][0] = x1 + style->borderWidth.left - 1; | |
577 | points[1][0] = x2 + 1 - style->borderWidth.right + 1; | |
578 | points[0][1] = points[1][1] = y1 - w + 1; | |
579 | points[2][0] = points[1][0] + style->borderWidth.right / 2; | |
580 | points[3][0] = points[0][0] - style->borderWidth.left / 2; | |
581 | points[2][1] = points[3][1] = points[0][1] + w/2; | |
578 | points[0].x = x1 + style->borderWidth.left - 1; | |
579 | points[1].x = x2 + 1 - style->borderWidth.right + 1; | |
580 | points[0].y = points[1].y = y1 - w + 1; | |
581 | points[2].x = points[1].x + style->borderWidth.right / 2; | |
582 | points[3].x = points[0].x - style->borderWidth.left / 2; | |
583 | points[2].y = points[3].y = points[0].y + w/2; | |
582 | 584 | shading = (ridge) ? Color::SHADING_LIGHT : Color::SHADING_DARK; |
583 | 585 | view->drawPolygon (style->borderColor.bottom, shading, filled, convex, |
584 | 586 | points, 4); |
592 | 594 | view->drawLine(style->borderColor.bottom, shading, x1, y1, x2, y2); |
593 | 595 | break; |
594 | 596 | } |
595 | points[0][0] = x2 + 2; | |
596 | points[1][0] = x1 - 1; | |
597 | points[0][1] = points[1][1] = y1 + 1; | |
598 | points[2][0] = points[1][0] + w_l; | |
599 | points[3][0] = points[0][0] - w_r; | |
600 | points[2][1] = points[3][1] = points[0][1] - w; | |
597 | points[0].x = x2 + 2; | |
598 | points[1].x = x1 - 1; | |
599 | points[0].y = points[1].y = y1 + 1; | |
600 | points[2].x = points[1].x + w_l; | |
601 | points[3].x = points[0].x - w_r; | |
602 | points[2].y = points[3].y = points[0].y - w; | |
601 | 603 | view->drawPolygon (style->borderColor.bottom, shading, filled, convex, |
602 | 604 | points, 4); |
603 | points[0][0] = x2 + 2 - style->borderWidth.right + w_r; | |
604 | points[1][0] = x1 - 1 + style->borderWidth.left - w_l; | |
605 | points[0][1] = points[1][1] = y1 + 1 - w - d; | |
606 | points[2][0] = x1 - 1 + style->borderWidth.left; | |
607 | points[3][0] = x2 + 2 - style->borderWidth.right; | |
608 | points[2][1] = points[3][1] = y1 + 1 - style->borderWidth.bottom; | |
605 | points[0].x = x2 + 2 - style->borderWidth.right + w_r; | |
606 | points[1].x = x1 - 1 + style->borderWidth.left - w_l; | |
607 | points[0].y = points[1].y = y1 + 1 - w - d; | |
608 | points[2].x = x1 - 1 + style->borderWidth.left; | |
609 | points[3].x = x2 + 2 - style->borderWidth.right; | |
610 | points[2].y = points[3].y = y1 + 1 - style->borderWidth.bottom; | |
609 | 611 | view->drawPolygon (style->borderColor.bottom, shading, filled, convex, |
610 | 612 | points, 4); |
611 | 613 | break; |
616 | 618 | int x1, int y1, int x2, int y2) |
617 | 619 | |
618 | 620 | { |
619 | int points[4][2], d, w; | |
621 | int d, w; | |
622 | Point points[4]; | |
620 | 623 | bool filled = true, convex = true; |
621 | 624 | bool ridge = false, inset = false, dotted = false; |
622 | 625 | Color::Shading shading = Color::SHADING_NORMAL; |
645 | 648 | if (style->borderWidth.left == 1) { /* 1 pixel line */ |
646 | 649 | view->drawLine(style->borderColor.left, shading, x1, y1, x2, y2); |
647 | 650 | } else { |
648 | points[0][0] = points[1][0] = x1; | |
649 | points[0][1] = y1 - 1; | |
650 | points[1][1] = y2 + 1; | |
651 | points[2][0] = points[3][0] = points[0][0] + style->borderWidth.left; | |
652 | points[2][1] = points[1][1] - style->borderWidth.bottom; | |
653 | points[3][1] = points[0][1] + style->borderWidth.top; | |
651 | points[0].x = points[1].x = x1; | |
652 | points[0].y = y1 - 1; | |
653 | points[1].y = y2 + 1; | |
654 | points[2].x = points[3].x = points[0].x + style->borderWidth.left; | |
655 | points[2].y = points[1].y - style->borderWidth.bottom; | |
656 | points[3].y = points[0].y + style->borderWidth.top; | |
654 | 657 | view->drawPolygon (style->borderColor.left, shading, filled, convex, |
655 | 658 | points, 4); |
656 | 659 | } |
660 | 663 | case BORDER_GROOVE: |
661 | 664 | w = style->borderWidth.left; |
662 | 665 | d = w & 1; |
663 | points[0][0] = points[1][0] = x1; | |
664 | points[0][1] = y1; | |
665 | points[1][1] = y2; | |
666 | points[2][0] = points[3][0] = x1 + w / 2 + d; | |
667 | points[2][1] = y2 - style->borderWidth.bottom / 2; | |
668 | points[3][1] = y1 + style->borderWidth.top / 2; | |
666 | points[0].x = points[1].x = x1; | |
667 | points[0].y = y1; | |
668 | points[1].y = y2; | |
669 | points[2].x = points[3].x = x1 + w / 2 + d; | |
670 | points[2].y = y2 - style->borderWidth.bottom / 2; | |
671 | points[3].y = y1 + style->borderWidth.top / 2; | |
669 | 672 | shading = (ridge) ? Color::SHADING_LIGHT : Color::SHADING_DARK; |
670 | 673 | view->drawPolygon (style->borderColor.left, shading, filled, convex, |
671 | 674 | points, 4); |
672 | points[0][0] = points[1][0] = x1 + w / 2 + d; | |
673 | points[0][1] = y1 + style->borderWidth.top / 2; | |
674 | points[1][1] = y2 - style->borderWidth.bottom / 2; | |
675 | points[2][0] = points[3][0] = x1 + w; | |
676 | points[2][1] = y2 - style->borderWidth.bottom; | |
677 | points[3][1] = y1 + style->borderWidth.top; | |
675 | points[0].x = points[1].x = x1 + w / 2 + d; | |
676 | points[0].y = y1 + style->borderWidth.top / 2; | |
677 | points[1].y = y2 - style->borderWidth.bottom / 2; | |
678 | points[2].x = points[3].x = x1 + w; | |
679 | points[2].y = y2 - style->borderWidth.bottom; | |
680 | points[3].y = y1 + style->borderWidth.top; | |
678 | 681 | shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT; |
679 | 682 | view->drawPolygon (style->borderColor.left, shading, filled, convex, |
680 | 683 | points, 4); |
688 | 691 | view->drawLine(style->borderColor.left, shading, x1, y1, x2, y2-1); |
689 | 692 | break; |
690 | 693 | } |
691 | points[0][0] = points[1][0] = x1; | |
692 | points[0][1] = y1 - 1; | |
693 | points[1][1] = y2 + 1; | |
694 | points[2][0] = points[3][0] = points[0][0] + w; | |
695 | points[2][1] = points[1][1] - w_b; | |
696 | points[3][1] = points[0][1] + w_t; | |
694 | points[0].x = points[1].x = x1; | |
695 | points[0].y = y1 - 1; | |
696 | points[1].y = y2 + 1; | |
697 | points[2].x = points[3].x = points[0].x + w; | |
698 | points[2].y = points[1].y - w_b; | |
699 | points[3].y = points[0].y + w_t; | |
697 | 700 | view->drawPolygon (style->borderColor.left, shading, filled, convex, |
698 | 701 | points, 4); |
699 | points[0][0] = points[1][0] = x1 + w + d; | |
700 | points[0][1] = y1 - 1 + style->borderWidth.top - w_t; | |
701 | points[1][1] = y2 + 1 - style->borderWidth.bottom + w_b; | |
702 | points[2][0] = points[3][0] = points[0][0] + w; | |
703 | points[2][1] = y2 + 1 - style->borderWidth.bottom; | |
704 | points[3][1] = y1 - 1 + style->borderWidth.top; | |
702 | points[0].x = points[1].x = x1 + w + d; | |
703 | points[0].y = y1 - 1 + style->borderWidth.top - w_t; | |
704 | points[1].y = y2 + 1 - style->borderWidth.bottom + w_b; | |
705 | points[2].x = points[3].x = points[0].x + w; | |
706 | points[2].y = y2 + 1 - style->borderWidth.bottom; | |
707 | points[3].y = y1 - 1 + style->borderWidth.top; | |
705 | 708 | view->drawPolygon (style->borderColor.left, shading, filled, convex, |
706 | 709 | points, 4); |
707 | 710 | break; |
712 | 715 | int x1, int y1, int x2, int y2) |
713 | 716 | |
714 | 717 | { |
715 | int points[4][2], d, w; | |
718 | int d, w; | |
719 | Point points[4]; | |
716 | 720 | const bool filled = true, convex = true; |
717 | 721 | bool ridge = false, inset = false, dotted = false; |
718 | 722 | Color::Shading shading = Color::SHADING_NORMAL; |
741 | 745 | if (style->borderWidth.right == 1) { /* 1 pixel line */ |
742 | 746 | view->drawLine(style->borderColor.right, shading, x1, y1, x2, y2); |
743 | 747 | } else { |
744 | points[0][0] = points[1][0] = x1 + 1; | |
745 | points[0][1] = y1 - 1; | |
746 | points[1][1] = y2 + 1; | |
747 | points[2][0] = points[3][0] = points[0][0]-style->borderWidth.right; | |
748 | points[2][1] = points[1][1] - style->borderWidth.bottom; | |
749 | points[3][1] = points[0][1] + style->borderWidth.top; | |
748 | points[0].x = points[1].x = x1 + 1; | |
749 | points[0].y = y1 - 1; | |
750 | points[1].y = y2 + 1; | |
751 | points[2].x = points[3].x = points[0].x-style->borderWidth.right; | |
752 | points[2].y = points[1].y - style->borderWidth.bottom; | |
753 | points[3].y = points[0].y + style->borderWidth.top; | |
750 | 754 | view->drawPolygon (style->borderColor.right, shading, filled, convex, |
751 | 755 | points,4); |
752 | 756 | } |
756 | 760 | case BORDER_GROOVE: |
757 | 761 | w = style->borderWidth.right; |
758 | 762 | d = w & 1; |
759 | points[0][0] = points[1][0] = x1 + 1; | |
760 | points[0][1] = y1; | |
761 | points[1][1] = y2; | |
762 | points[2][0] = points[3][0] = points[0][0] - w / 2 - d; | |
763 | points[2][1] = y2 - style->borderWidth.bottom / 2; | |
764 | points[3][1] = points[0][1] + style->borderWidth.top / 2; | |
763 | points[0].x = points[1].x = x1 + 1; | |
764 | points[0].y = y1; | |
765 | points[1].y = y2; | |
766 | points[2].x = points[3].x = points[0].x - w / 2 - d; | |
767 | points[2].y = y2 - style->borderWidth.bottom / 2; | |
768 | points[3].y = points[0].y + style->borderWidth.top / 2; | |
765 | 769 | shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT; |
766 | 770 | view->drawPolygon (style->borderColor.right, shading, filled, convex, |
767 | 771 | points, 4); |
768 | points[0][0] = points[1][0] = x1 + 1 - w / 2 - d; | |
769 | points[0][1] = y1 + style->borderWidth.top / 2; | |
770 | points[1][1] = y2 - style->borderWidth.bottom / 2; | |
771 | points[2][0] = points[3][0] = x1 + 1 - w; | |
772 | points[2][1] = y2 - style->borderWidth.bottom; | |
773 | points[3][1] = y1 + style->borderWidth.top; | |
772 | points[0].x = points[1].x = x1 + 1 - w / 2 - d; | |
773 | points[0].y = y1 + style->borderWidth.top / 2; | |
774 | points[1].y = y2 - style->borderWidth.bottom / 2; | |
775 | points[2].x = points[3].x = x1 + 1 - w; | |
776 | points[2].y = y2 - style->borderWidth.bottom; | |
777 | points[3].y = y1 + style->borderWidth.top; | |
774 | 778 | shading = (ridge) ? Color::SHADING_LIGHT: Color::SHADING_DARK; |
775 | 779 | view->drawPolygon (style->borderColor.right, shading, filled, convex, |
776 | 780 | points, 4); |
784 | 788 | view->drawLine(style->borderColor.right, shading, x1, y1, x2, y2); |
785 | 789 | break; |
786 | 790 | } |
787 | points[0][0] = points[1][0] = x1 + 1; | |
788 | points[0][1] = y1 - 1; | |
789 | points[1][1] = y2 + 1; | |
790 | points[2][0] = points[3][0] = points[0][0] - w; | |
791 | points[2][1] = points[1][1] - w_b; | |
792 | points[3][1] = points[0][1] + w_t; | |
791 | points[0].x = points[1].x = x1 + 1; | |
792 | points[0].y = y1 - 1; | |
793 | points[1].y = y2 + 1; | |
794 | points[2].x = points[3].x = points[0].x - w; | |
795 | points[2].y = points[1].y - w_b; | |
796 | points[3].y = points[0].y + w_t; | |
793 | 797 | view->drawPolygon (style->borderColor.right, shading, filled, convex, |
794 | 798 | points, 4); |
795 | points[0][0] = points[1][0] = x1 + 1 - w - d; | |
796 | points[0][1] = y1 - 1 + style->borderWidth.top - w_t; | |
797 | points[1][1] = y2 + 1 - style->borderWidth.bottom + w_b; | |
798 | points[2][0] = points[3][0] = points[0][0] - w; | |
799 | points[2][1] = y2 + 1 - style->borderWidth.bottom; | |
800 | points[3][1] = y1 - 1 + style->borderWidth.top; | |
799 | points[0].x = points[1].x = x1 + 1 - w - d; | |
800 | points[0].y = y1 - 1 + style->borderWidth.top - w_t; | |
801 | points[1].y = y2 + 1 - style->borderWidth.bottom + w_b; | |
802 | points[2].x = points[3].x = points[0].x - w; | |
803 | points[2].y = y2 + 1 - style->borderWidth.bottom; | |
804 | points[3].y = y1 - 1 + style->borderWidth.top; | |
801 | 805 | view->drawPolygon (style->borderColor.right, shading, filled, convex, |
802 | 806 | points, 4); |
803 | 807 | break; |
25 | 25 | #include <stdio.h> |
26 | 26 | #include <limits.h> |
27 | 27 | |
28 | /* | |
29 | * Local variables | |
30 | */ | |
31 | /* The tooltip under mouse pointer in current textblock. No ref. hold. | |
32 | * (having one per view looks not worth the extra clutter). */ | |
33 | static dw::core::style::Tooltip *hoverTooltip = NULL; | |
34 | ||
35 | ||
36 | ||
28 | 37 | using namespace lout; |
29 | 38 | |
30 | 39 | namespace dw { |
79 | 88 | availAscent = 100; |
80 | 89 | availDescent = 0; |
81 | 90 | |
82 | hoverTooltip = NULL; | |
83 | ||
84 | 91 | this->limitTextWidth = limitTextWidth; |
85 | 92 | |
86 | 93 | for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) { |
94 | 101 | |
95 | 102 | Textblock::~Textblock () |
96 | 103 | { |
97 | //_MSG ("Textblock::~Textblock\n"); | |
104 | _MSG("Textblock::~Textblock\n"); | |
105 | ||
106 | /* make sure not to call a free'd tooltip (very fast overkill) */ | |
107 | hoverTooltip = NULL; | |
98 | 108 | |
99 | 109 | for (int i = 0; i < words->size(); i++) { |
100 | 110 | Word *word = words->getRef (i); |
365 | 375 | * http://www.dillo.org/test/img/ */ |
366 | 376 | childAllocation.y = |
367 | 377 | lineYOffsetCanvasAllocation (line, allocation) |
368 | + (line->boxAscent - word->size.ascent); | |
369 | // - word->content.widget->getStyle()->margin.top; | |
378 | + (line->boxAscent - word->size.ascent) | |
379 | - word->content.widget->getStyle()->margin.top; | |
370 | 380 | childAllocation.width = word->size.width; |
371 | childAllocation.ascent = word->size.ascent; | |
372 | // + word->content.widget->getStyle()->margin.top; | |
373 | childAllocation.descent = word->size.descent; | |
374 | // + word->content.widget->getStyle()->margin.bottom; | |
381 | childAllocation.ascent = word->size.ascent | |
382 | + word->content.widget->getStyle()->margin.top; | |
383 | childAllocation.descent = word->size.descent | |
384 | + word->content.widget->getStyle()->margin.bottom; | |
375 | 385 | |
376 | 386 | oldChildAllocation = word->content.widget->getAllocation(); |
377 | 387 | |
535 | 545 | return sendSelectionEvent (core::SelectionState::BUTTON_RELEASE, event); |
536 | 546 | } |
537 | 547 | |
548 | /* | |
549 | * Handle motion inside the widget | |
550 | * (special care is necessary when switching from another widget, | |
551 | * because hoverLink and hoverTooltip are meaningless then). | |
552 | */ | |
538 | 553 | bool Textblock::motionNotifyImpl (core::EventMotion *event) |
539 | 554 | { |
540 | 555 | if (event->state & core::BUTTON1_MASK) |
556 | 571 | hoverLink = style->x_link; |
557 | 572 | hoverTooltip = style->x_tooltip; |
558 | 573 | } |
574 | ||
559 | 575 | // Show/hide tooltip |
560 | 576 | if (tooltipOld != hoverTooltip) { |
561 | 577 | if (tooltipOld) |
565 | 581 | } else if (hoverTooltip) |
566 | 582 | hoverTooltip->onMotion (); |
567 | 583 | |
568 | if (hoverLink != linkOld) | |
584 | _MSG("MN tb=%p tooltipOld=%p hoverTooltip=%p\n", | |
585 | this, tooltipOld, hoverTooltip); | |
586 | if (hoverLink != linkOld) { | |
587 | /* LinkEnter with hoverLink == -1 is the same as LinkLeave */ | |
569 | 588 | return layout->emitLinkEnter (this, hoverLink, -1, -1, -1); |
570 | else | |
589 | } else { | |
571 | 590 | return hoverLink != -1; |
591 | } | |
572 | 592 | } |
573 | 593 | } |
574 | 594 | |
575 | 595 | void Textblock::enterNotifyImpl (core::EventCrossing *event) |
576 | 596 | { |
597 | _MSG(" tb=%p, ENTER NotifyImpl hoverTooltip=%p\n", this, hoverTooltip); | |
598 | /* reset hoverLink so linkEnter is detected */ | |
599 | hoverLink = -2; | |
577 | 600 | } |
578 | 601 | |
579 | 602 | void Textblock::leaveNotifyImpl (core::EventCrossing *event) |
580 | 603 | { |
581 | hoverLink = -1; | |
582 | (void) layout->emitLinkEnter (this, hoverLink, -1, -1, -1); | |
604 | _MSG(" tb=%p, LEAVE NotifyImpl: hoverTooltip=%p\n", this, hoverTooltip); | |
605 | ||
606 | /* leaving the viewport can't be handled by motionNotifyImpl() */ | |
607 | if (hoverLink >= 0) | |
608 | layout->emitLinkEnter (this, -1, -1, -1, -1); | |
609 | ||
583 | 610 | if (hoverTooltip) { |
584 | 611 | hoverTooltip->onLeave(); |
585 | 612 | hoverTooltip = NULL; |
962 | 989 | // lastLine->boxDescent); |
963 | 990 | |
964 | 991 | if (word->content.type == core::Content::WIDGET) { |
992 | int collapseMarginTop = 0; | |
993 | ||
965 | 994 | lastLine->marginDescent = |
966 | 995 | misc::max (lastLine->marginDescent, |
967 | 996 | word->size.descent + |
968 | 997 | word->content.widget->getStyle()->margin.bottom); |
969 | 998 | |
970 | //DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1, | |
971 | // lastLine->descent); | |
972 | ||
973 | /* If the widget is not in the first line of the paragraph, its top | |
974 | * margin may make the line higher. | |
975 | */ | |
976 | if (lines->size () > 1) { | |
977 | /* Here, we know already what the break and the bottom margin | |
978 | * contributed to the space before this line. | |
979 | */ | |
980 | lastLine->boxAscent = | |
999 | if (lines->size () == 1 && | |
1000 | word->content.widget->blockLevel () && | |
1001 | getStyle ()->borderWidth.top == 0 && | |
1002 | getStyle ()->padding.top == 0) { | |
1003 | // collapse top margin of parent element with top margin of first child | |
1004 | // see: http://www.w3.org/TR/CSS21/box.html#collapsing-margins | |
1005 | collapseMarginTop = getStyle ()->margin.top; | |
1006 | } | |
1007 | ||
1008 | lastLine->boxAscent = | |
981 | 1009 | misc::max (lastLine->boxAscent, |
1010 | word->size.ascent, | |
982 | 1011 | word->size.ascent |
983 | + word->content.widget->getStyle()->margin.top); | |
984 | ||
985 | //DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1, | |
986 | // lastLine->boxAscent); | |
987 | } | |
1012 | + word->content.widget->getStyle()->margin.top | |
1013 | - collapseMarginTop); | |
1014 | ||
988 | 1015 | } else { |
989 | 1016 | lastLine->marginDescent = |
990 | 1017 | misc::max (lastLine->marginDescent, lastLine->boxDescent); |
1095 | 1122 | widget->setAscent (availAscent); |
1096 | 1123 | widget->setDescent (availDescent); |
1097 | 1124 | widget->sizeRequest (size); |
1098 | // size->ascent -= wstyle->margin.top; | |
1099 | // size->descent -= wstyle->margin.bottom; | |
1100 | 1125 | } else { |
1101 | /* TODO: Use margin.{top|bottom} here, like above. | |
1102 | * (No harm for the next future.) */ | |
1103 | 1126 | if (wstyle->width == core::style::LENGTH_AUTO || |
1104 | 1127 | wstyle->height == core::style::LENGTH_AUTO) |
1105 | 1128 | widget->sizeRequest (&requisition); |
1130 | 1153 | size->descent = (int) (len * availDescent); |
1131 | 1154 | } |
1132 | 1155 | } |
1156 | ||
1157 | /* ascent and descent in words do not contain margins. */ | |
1158 | size->ascent -= wstyle->margin.top; | |
1159 | size->descent -= wstyle->margin.bottom; | |
1133 | 1160 | } |
1134 | 1161 | |
1135 | 1162 | /** |
250 | 250 | struct {int index, nChar;} |
251 | 251 | hlStart[core::HIGHLIGHT_NUM_LAYERS], hlEnd[core::HIGHLIGHT_NUM_LAYERS]; |
252 | 252 | |
253 | int hoverLink; /* The link under the button. */ | |
254 | core::style::Tooltip *hoverTooltip; /* The tooltip under the button. No ref | |
255 | * hold. */ | |
253 | int hoverLink; /* The link under the mouse pointer */ | |
256 | 254 | |
257 | 255 | |
258 | 256 | void queueDrawRange (int index1, int index2); |
140 | 140 | if (points->size()) { |
141 | 141 | int i; |
142 | 142 | const bool filled = false, convex = false; |
143 | int (*pointArray)[2] = | |
144 | (int (*)[2]) malloc(points->size() * sizeof(*pointArray)); | |
143 | Point *pointArray = (Point *)malloc(points->size()*sizeof(struct Point)); | |
145 | 144 | |
146 | 145 | for (i = 0; i < points->size(); i++) { |
147 | pointArray[i][0] = x + points->getRef(i)->x; | |
148 | pointArray[i][1] = y + points->getRef(i)->y; | |
146 | pointArray[i].x = x + points->getRef(i)->x; | |
147 | pointArray[i].y = y + points->getRef(i)->y; | |
149 | 148 | } |
150 | 149 | view->drawPolygon(style->color, core::style::Color::SHADING_NORMAL, |
151 | 150 | filled, convex, pointArray, i); |
172 | 172 | int angle1, int angle2) = 0; |
173 | 173 | virtual void drawPolygon (style::Color *color, |
174 | 174 | style::Color::Shading shading, |
175 | bool filled, bool convex, int points[][2], | |
175 | bool filled, bool convex, Point *points, | |
176 | 176 | int npoints) = 0; |
177 | 177 | virtual void drawText (style::Font *font, |
178 | 178 | style::Color *color, |
179 | 179 | style::Color::Shading shading, |
180 | 180 | int x, int y, const char *text, int len) = 0; |
181 | ||
181 | virtual void drawSimpleWrappedText (style::Font *font, style::Color *color, | |
182 | style::Color::Shading shading, | |
183 | int x, int y, int w, int h, | |
184 | const char *text) = 0; | |
182 | 185 | virtual void drawImage (Imgbuf *imgbuf, int xRoot, int yRoot, |
183 | 186 | int x, int y, int width, int height) = 0; |
184 | 187 |
60 | 60 | " <table border='0' cellspacing='0' cellpadding='2'><tr>\n" |
61 | 61 | " <td>\n" |
62 | 62 | " <td>\n" |
63 | " <a href='http://www.dillo.org/dillo2-help.html'>\n" | |
63 | " <a href='http://www.dillo.org/dillo3-help.html'>\n" | |
64 | 64 | " Help</a>\n" |
65 | 65 | " <tr>\n" |
66 | 66 | " <td> \n" |
116 | 116 | " <tr>\n" |
117 | 117 | " <td> \n" |
118 | 118 | " <td>\n" |
119 | " <a href='http://slashdot.org/'>Slashdot</a>\n" | |
120 | " <tr>\n" | |
121 | " <td> \n" | |
122 | " <td>\n" | |
123 | 119 | " <a href='http://www.linux.org.uk/Portaloo.cs'>Linux.org.uk</a>\n" |
120 | " <tr>\n" | |
121 | " <td> \n" | |
122 | " <td>\n" | |
123 | " <a href='http://www.mathaba.net/'>Mathaba</a>\n" | |
124 | " <tr>\n" | |
124 | 125 | " <tr>\n" |
125 | 126 | " <td> \n" |
126 | 127 | " <td>\n" |
151 | 152 | " <td bgcolor='#FFFFFF'>\n" |
152 | 153 | " <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n" |
153 | 154 | " <table border='0' cellpadding='2'><tr>\n" |
154 | " <td> \n" | |
155 | " <td><a href='http://www.google.com/'>Google</a>\n" | |
156 | " <tr>\n" | |
157 | " <td> \n" | |
158 | " <td><a href='http://www.wikipedia.org/'>Wikipedia</a>\n" | |
159 | " <tr>\n" | |
160 | 155 | " <td> \n" |
161 | 156 | " <td><a href='http://www.gutenberg.org/'>P. Gutenberg</a>\n" |
162 | 157 | " <tr>\n" |
239 | 234 | "<tr>\n" |
240 | 235 | " <td bgcolor='#CCCCCC'>\n" |
241 | 236 | " <h4>Release overview</h4>\n" |
242 | " February 11, 2010\n" | |
237 | " August ??, 2011\n" | |
243 | 238 | "<tr>\n" |
244 | 239 | " <td bgcolor='#FFFFFF'>\n" |
245 | 240 | " <table border='0' cellspacing='0' cellpadding='5'>\n" |
246 | 241 | " <tr>\n" |
247 | 242 | " <td>\n" |
248 | 243 | "<p>\n" |
249 | "This release features a major overhaul of the cookies subsystem,\n" | |
250 | "a reimplementation of the DPI API, a configurable connection limit,\n" | |
251 | "and various CSS improvements.\n" | |
252 | "<p>\n" | |
253 | "Remember that the dillo project uses a release model where every new\n" | |
254 | "version shall be better than the last.\n" | |
255 | "<EM>Keep up with the latest one!</EM>\n" | |
244 | "Dillo-3.0 is a port to FLTK-1.3, which is big news because FLTK-1.3.0 was\n" | |
245 | "<a href='http://fltk.org/articles.php?L1086'>released</a>\n" | |
246 | "in June, clearing the way for Dillo to return to those distributions\n" | |
247 | "which had excluded Dillo2 due to FLTK2 never being officially released.\n" | |
248 | "<p>\n" | |
249 | "Dillo-3.0 also has plenty of improvements and bugfixes.\n" | |
250 | "<p>\n" | |
251 | "After this release, the core team will focus on implementing the CSS\n" | |
252 | "feature of floating elements. This will <em>greatly</em> improve dillo's\n" | |
253 | "web page rendering since many sites have adopted floats instead of tables.\n" | |
254 | "<p>\n" | |
255 | "The new dillo3 has shown excellent stability in our experience.\n" | |
256 | "The core team welcomes developers willing to join our workforce.\n" | |
257 | "<p>\n" | |
256 | 258 | " </table>\n" |
257 | 259 | "</table>\n" |
258 | 260 | "</table>\n" |
273 | 275 | " <tr>\n" |
274 | 276 | " <td>\n" |
275 | 277 | "<ul>\n" |
276 | "<li>Added keybindings for scrolling.\n" | |
277 | "<li>Help button and local help file.\n" | |
278 | "<li>Add support for multiple class names in CSS.\n" | |
279 | "<li>Fix X11 coordinate overflows.\n" | |
280 | "<li>Improve CSS font parsing.\n" | |
281 | "<li>Enable font face setting via <font> element.\n" | |
282 | "<li>Ignore XML comment markers in CSS.\n" | |
283 | "<li>Fix user agent style for nested <ul>.\n" | |
284 | "<li>Handle signed chars. Added dIsspace() and dIsalnum() to dlib.\n" | |
285 | "<li>Changed the CCCs to build in one step (for both HTTP and DPI).\n" | |
286 | "<li>Remove the empty cache entry lingering after connection abort.\n" | |
287 | "<li>Fixed URL unescaping in the datauri DPI.\n" | |
288 | "<li>Changed and reimplemented the DPI API.\n" | |
289 | "<li>Allow linebreaks around Chinese/Japanese characters.\n" | |
290 | "<li>Fix scrolling for text search.\n" | |
291 | "<li>Tooltips.\n" | |
292 | "<li>Enable popup menu below bottom of page content.\n" | |
293 | "<li>Handle JPEGs with CMYK color space.\n" | |
294 | "<li>General cookies overhaul.\n" | |
295 | "<li>Fixed a bug in w3c_mode.\n" | |
296 | "<li>Limit number of simultaneous connections.\n" | |
278 | "<li>Ported Dillo to FLTK-1.3.\n" | |
279 | "<li>Native build on OSX.\n" | |
280 | "<li>Default binding for close-all changed from Alt-q to Ctrl-q.\n" | |
281 | "<li>Default binding for close-tab changed from Ctrl-q to Ctrl-w.\n" | |
282 | "<li>Default binding for left-tab changed to Shift-Ctrl-Tab.\n" | |
283 | "<li>'hide-panels' key action now hides the findbar if present,\n" | |
284 | " and toggles display of the control panels otherwise.\n" | |
285 | "<li>Allow multiple search engines to be set in dillorc, with a menu\n" | |
286 | " in the web search dialog to select between them.\n" | |
287 | "<li>Added an optional label to dillorc's search_url.\n" | |
288 | " Format: \"[<label> ]<url>\"\n" | |
289 | "<li>Removed 'large' option of panel_size preference.\n" | |
290 | "<li>Add right_click_closes_tab preference (default is middle click).\n" | |
291 | "<li>Allow binding to non-ASCII keys and multimedia keys.\n" | |
292 | "<li>Removed --enable-ansi configure option.\n" | |
293 | "<li>Avoid a certificate dialog storm on some HTTPS sites (BUG#868).\n" | |
294 | "<li>Enable line wrapping for <textarea>. (BUG#903)\n" | |
295 | "<li>Rewrote the User Interface: much simpler design and event handling.\n" | |
296 | "<li>Avoid double render after going Back or Forward\n" | |
297 | " (it takes half the time now!)\n" | |
298 | "<li>Added on-the-fly panel resize (tiny/small/medium and normal/small icons).\n" | |
299 | "<li>Implemented a custom tabs handler (to allow fine control of it).\n" | |
300 | "<li>Rewrote dw's crossing-events dispatcher (avoids redundant events).\n" | |
301 | "<li>Fixed a years old bug: stamped tooltips when scrolling with keyboard.\n" | |
302 | "<li>Fixed a border case in URL resolver: empty path + {query|fragment}\n" | |
303 | " (BUG#948)\n" | |
304 | "<li>Cancel the expected URL after offering a download (BUG#982)\n" | |
305 | "<li>Eliminated a pack of 22 compiler warnings (gcc-4.6.1 amd64)\n" | |
306 | "<li>Limit saved cookie size.\n" | |
307 | "<li>Wrap image alt text.\n" | |
308 | "<li>Added support for CSS adjacent sibling selectors.\n" | |
309 | "<li>Collapse parent's and first child's top margin.\n" | |
297 | 310 | "</ul>\n" |
298 | 311 | " </table>\n" |
299 | 312 | "</table>\n" |
364 | 364 | DataBuf *dbuf; |
365 | 365 | |
366 | 366 | /* Create the query */ |
367 | query = a_Http_make_query_str(S->web->url, S->flags & HTTP_SOCKET_USE_PROXY); | |
367 | query = a_Http_make_query_str(S->web->url,S->flags & HTTP_SOCKET_USE_PROXY); | |
368 | 368 | dbuf = a_Chain_dbuf_new(query->str, query->len, 0); |
369 | 369 | |
370 | 370 | /* actually this message is sent too early. |
54 | 54 | bw->nav_stack_ptr = -1; |
55 | 55 | |
56 | 56 | /* Init expect */ |
57 | bw->nav_expecting = FALSE; | |
58 | 57 | bw->nav_expect_url = NULL; |
59 | 58 | |
60 | 59 | bw->redirect_level = 0; |
312 | 311 | return NULL; |
313 | 312 | } |
314 | 313 | |
314 | /* expect API ------------------------------------------------------------- */ | |
315 | ||
316 | void a_Bw_expect(BrowserWindow *bw, const DilloUrl *url) | |
317 | { | |
318 | a_Url_free(bw->nav_expect_url); | |
319 | bw->nav_expect_url = a_Url_dup(url); | |
320 | } | |
321 | ||
322 | void a_Bw_cancel_expect(BrowserWindow *bw) | |
323 | { | |
324 | a_Url_free(bw->nav_expect_url); | |
325 | bw->nav_expect_url = NULL; | |
326 | } | |
327 | ||
328 | bool_t a_Bw_expecting(BrowserWindow *bw) | |
329 | { | |
330 | return (bw->nav_expect_url != NULL); | |
331 | } | |
332 | ||
333 | const DilloUrl *a_Bw_expected_url(BrowserWindow *bw) | |
334 | { | |
335 | return bw->nav_expect_url; | |
336 | } | |
337 |
45 | 45 | /* 'nav_stack_ptr' refers to what's being displayed */ |
46 | 46 | int nav_stack_ptr; /* [0 based; -1 = empty] */ |
47 | 47 | /* When the user clicks a link, the URL isn't pushed directly to history; |
48 | * nav_expect_url holds it until the first answer-bytes are got. Only then | |
49 | * it is sent to history and referenced at the top of nav_stack */ | |
48 | * nav_expect_url holds it until a dw is assigned to it. Only then an entry | |
49 | * is made in history and referenced at the top of nav_stack */ | |
50 | 50 | DilloUrl *nav_expect_url; |
51 | /* 'nav_expecting' is true while dillo waits for non-cached URL data, | |
52 | * until a dw is assigned to it. */ | |
53 | bool_t nav_expecting; | |
54 | 51 | |
55 | 52 | /* Counter for the number of hops on a redirection. Used to stop |
56 | 53 | * redirection loops (accounts for WEB_RootUrl only) */ |
87 | 84 | void a_Bw_remove_doc(BrowserWindow *bw, void *vdoc); |
88 | 85 | void a_Bw_add_url(BrowserWindow *bw, const DilloUrl *Url); |
89 | 86 | void a_Bw_cleanup(BrowserWindow *bw); |
87 | /* expect API */ | |
88 | void a_Bw_expect(BrowserWindow *bw, const DilloUrl *Url); | |
89 | void a_Bw_cancel_expect(BrowserWindow *bw); | |
90 | bool_t a_Bw_expecting(BrowserWindow *bw); | |
91 | const DilloUrl *a_Bw_expected_url(BrowserWindow *bw); | |
90 | 92 | |
91 | 93 | typedef void (*BwCallback_t)(BrowserWindow *bw, const void *data); |
92 | 94 |
891 | 891 | " at: %s\n", URL_STR_(entry->Url)); |
892 | 892 | MSG("entry->ExpectedSize = %d, entry->TransferSize = %d\n", |
893 | 893 | entry->ExpectedSize, entry->TransferSize); |
894 | } | |
895 | if (!entry->TransferSize && !(entry->Flags & CA_Redirect) && | |
896 | (entry->Flags & WEB_RootUrl)) { | |
897 | char *eol = strchr(entry->Header->str, '\n'); | |
898 | if (eol) { | |
899 | char *status_line = dStrndup(entry->Header->str, | |
900 | eol - entry->Header->str); | |
901 | MSG_HTTP("Body was empty. Server sent status: %s\n", status_line); | |
902 | dFree(status_line); | |
903 | } | |
894 | 904 | } |
895 | 905 | entry->Flags |= CA_GotData; |
896 | 906 | entry->Flags &= ~CA_Stopped; /* it may catch up! */ |
301 | 301 | char *proxy_connect = a_Http_make_connect_str(web->url); |
302 | 302 | Dstr *http_query = a_Http_make_query_str(web->url, FALSE); |
303 | 303 | /* BUG: embedded NULLs in query data will truncate message */ |
304 | ||
305 | /* BUG: WORKAROUND: request to only check the root URL's certificate. | |
306 | * This avoids the dialog bombing that stems from loading multiple | |
307 | * https images/resources in a single page. A proper fix would take | |
308 | * either to implement the https-dpi as a server (with state), | |
309 | * or to move back https handling into dillo. */ | |
304 | 310 | if (proxy_connect) { |
305 | 311 | const char *proxy_urlstr = a_Http_get_proxy_urlstr(); |
306 | 312 | cmd = a_Dpip_build_cmd("cmd=%s proxy_url=%s proxy_connect=%s " |
307 | "url=%s query=%s", "open_url", proxy_urlstr, | |
313 | "url=%s query=%s check_cert=%s", | |
314 | "open_url", proxy_urlstr, | |
308 | 315 | proxy_connect, URL_STR(web->url), |
309 | http_query->str); | |
316 | http_query->str, | |
317 | (web->flags & WEB_RootUrl) ? "true" : "false"); | |
310 | 318 | } else { |
311 | cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s", | |
312 | "open_url", URL_STR(web->url),http_query->str); | |
319 | cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s check_cert=%s", | |
320 | "open_url", URL_STR(web->url),http_query->str, | |
321 | (web->flags & WEB_RootUrl) ? "true" : "false"); | |
313 | 322 | } |
314 | 323 | dFree(proxy_connect); |
315 | 324 | dStr_free(http_query, 1); |
103 | 103 | cs = selectorList->getRef (selectorList->size () - 1); |
104 | 104 | |
105 | 105 | cs->notMatchingBefore = -1; |
106 | cs->combinator = CHILD; | |
106 | 107 | cs->selector = new CssSimpleSelector (); |
107 | 108 | }; |
108 | 109 | |
132 | 133 | |
133 | 134 | switch (comb) { |
134 | 135 | case CHILD: |
136 | case ADJACENT_SIBLING: | |
135 | 137 | if (!sel->match (node)) |
136 | 138 | return false; |
137 | 139 | break; |
155 | 157 | } |
156 | 158 | |
157 | 159 | comb = cs->combinator; |
158 | node = docTree->parent (node); | |
160 | ||
161 | if (comb == ADJACENT_SIBLING) | |
162 | node = docTree->sibling (node); | |
163 | else | |
164 | node = docTree->parent (node); | |
159 | 165 | } |
160 | 166 | |
161 | 167 | return true; |
198 | 204 | break; |
199 | 205 | case DESCENDANT: |
200 | 206 | fprintf (stderr, "\" \" "); |
207 | break; | |
208 | case ADJACENT_SIBLING: | |
209 | fprintf (stderr, "+ "); | |
201 | 210 | break; |
202 | 211 | default: |
203 | 212 | fprintf (stderr, "? "); |
488 | 497 | } |
489 | 498 | } |
490 | 499 | |
491 | CssStyleSheet *CssContext::userAgentStyle; | |
492 | CssStyleSheet *CssContext::userStyle; | |
493 | CssStyleSheet *CssContext::userImportantStyle; | |
494 | ||
495 | 500 | CssContext::CssContext () { |
496 | 501 | pos = 0; |
497 | 502 | |
498 | for (int o = CSS_PRIMARY_USER_AGENT; o < CSS_PRIMARY_LAST; o++) | |
499 | sheet[o] = NULL; | |
500 | ||
501 | if (userAgentStyle == NULL) { | |
502 | userAgentStyle = new CssStyleSheet (); | |
503 | userStyle = new CssStyleSheet (); | |
504 | userImportantStyle = new CssStyleSheet (); | |
505 | ||
506 | sheet[CSS_PRIMARY_USER_AGENT] = userAgentStyle; | |
507 | sheet[CSS_PRIMARY_USER] = userStyle; | |
508 | sheet[CSS_PRIMARY_USER_IMPORTANT] = userImportantStyle; | |
509 | ||
510 | buildUserAgentStyle (); | |
511 | buildUserStyle (); | |
512 | } | |
513 | ||
514 | sheet[CSS_PRIMARY_USER_AGENT] = userAgentStyle; | |
515 | sheet[CSS_PRIMARY_USER] = userStyle; | |
516 | sheet[CSS_PRIMARY_USER_IMPORTANT] = userImportantStyle; | |
503 | memset (sheet, 0, sizeof(sheet)); | |
504 | sheet[CSS_PRIMARY_USER_AGENT] = new CssStyleSheet (); | |
505 | sheet[CSS_PRIMARY_USER] = new CssStyleSheet (); | |
506 | sheet[CSS_PRIMARY_USER_IMPORTANT] = new CssStyleSheet (); | |
507 | ||
508 | buildUserAgentStyle (); | |
509 | buildUserStyle (); | |
517 | 510 | } |
518 | 511 | |
519 | 512 | CssContext::~CssContext () { |
520 | 513 | for (int o = CSS_PRIMARY_USER_AGENT; o < CSS_PRIMARY_LAST; o++) |
521 | if (sheet[o] != userAgentStyle && sheet[o] != userStyle && | |
522 | sheet[o] != userImportantStyle) | |
523 | delete sheet[o]; | |
514 | delete sheet[o]; | |
524 | 515 | } |
525 | 516 | |
526 | 517 | /** |
457 | 457 | */ |
458 | 458 | class CssContext { |
459 | 459 | private: |
460 | static CssStyleSheet *userAgentStyle; | |
461 | static CssStyleSheet *userStyle; | |
462 | static CssStyleSheet *userImportantStyle; | |
463 | 460 | CssStyleSheet *sheet[CSS_PRIMARY_USER_IMPORTANT + 1]; |
464 | 461 | int pos; |
465 | 462 |
1288 | 1288 | } else if (ttype == CSS_TK_CHAR && tval[0] == '>') { |
1289 | 1289 | selector->addSimpleSelector (CssSelector::CHILD); |
1290 | 1290 | nextToken(); |
1291 | } else if (ttype == CSS_TK_CHAR && tval[0] == '+') { | |
1292 | selector->addSimpleSelector (CssSelector::ADJACENT_SIBLING); | |
1293 | nextToken(); | |
1291 | 1294 | } else if (ttype != CSS_TK_END && spaceSeparated) { |
1292 | 1295 | selector->addSimpleSelector (CssSelector::DESCENDANT); |
1293 | 1296 | } else { |
88 | 88 | if (e == FL_KEYBOARD && |
89 | 89 | (Fl::event_key() == FL_Enter || Fl::event_key() == FL_Down) && |
90 | 90 | (Fl::event_state() & (FL_SHIFT|FL_CTRL|FL_ALT|FL_META)) == 0) { |
91 | MSG("CustChoice: ENTER\n"); | |
92 | 91 | return Fl_Choice::handle(FL_PUSH); |
93 | 92 | } |
94 | 93 | return Fl_Choice::handle(e); |
163 | 162 | for (int i = 0, j = 0; i < n_it; i++) { |
164 | 163 | char *label, *url, *source; |
165 | 164 | source = (char *)dList_nth_data(prefs.search_urls, i); |
166 | if (a_Misc_parse_search_url(source, &label, &url) < 0) | |
165 | if (!source || a_Misc_parse_search_url(source, &label, &url) < 0) | |
167 | 166 | continue; |
168 | 167 | pm[j++].label(FL_NORMAL_LABEL, strdup(label)); |
169 | 168 | } |
190 | 189 | window->show(); |
191 | 190 | while (window->shown()) |
192 | 191 | Fl::wait(); |
193 | _MSG("a_Dialog_input answer = %d\n", input_answer); | |
194 | 192 | if (input_answer == 1) { |
195 | 193 | /* we have a string, save it */ |
196 | 194 | dFree(input_str); |
197 | 195 | input_str = dStrdup(c_inp->value()); |
198 | MSG("a_Dialog_input value() = %d\n", ch->value()); | |
199 | 196 | prefs.search_url_idx = ch->value(); |
200 | 197 | } |
201 | 198 | delete window; |
5 | 5 | class DoctreeNode { |
6 | 6 | public: |
7 | 7 | DoctreeNode *parent; |
8 | DoctreeNode *sibling; | |
9 | DoctreeNode *lastChild; | |
8 | 10 | int num; // unique ascending id |
9 | 11 | int element; |
10 | 12 | lout::misc::SimpleVector<char*> *klass; |
13 | 15 | |
14 | 16 | DoctreeNode () { |
15 | 17 | parent = NULL; |
18 | sibling = NULL; | |
19 | lastChild = NULL; | |
16 | 20 | klass = NULL; |
17 | 21 | pseudo = NULL; |
18 | 22 | id = NULL; |
19 | 23 | element = 0; |
20 | 24 | }; |
25 | ||
26 | ~DoctreeNode () { | |
27 | dFree ((void*) id); | |
28 | while (lastChild) { | |
29 | DoctreeNode *n = lastChild; | |
30 | lastChild = lastChild->sibling; | |
31 | delete n; | |
32 | } | |
33 | if (klass) { | |
34 | for (int i = 0; i < klass->size (); i++) | |
35 | dFree (klass->get(i)); | |
36 | delete klass; | |
37 | } | |
38 | } | |
21 | 39 | }; |
22 | 40 | |
23 | 41 | /** |
25 | 43 | * |
26 | 44 | * The Doctree class defines the interface to the parsed HTML document tree |
27 | 45 | * as it is used for CSS selector matching. |
28 | * Currently the Doctree can be represented as stack, however to support | |
29 | * CSS adjacent siblings or for future JavaScript support it may have to | |
30 | * be extended to a real tree. | |
31 | 46 | */ |
32 | 47 | class Doctree { |
33 | 48 | private: |
34 | 49 | DoctreeNode *topNode; |
50 | DoctreeNode *rootNode; | |
35 | 51 | int num; |
36 | 52 | |
37 | 53 | public: |
38 | 54 | Doctree () { |
39 | topNode = NULL; | |
55 | rootNode = new DoctreeNode; | |
56 | topNode = rootNode; | |
40 | 57 | num = 0; |
41 | 58 | }; |
42 | ~Doctree () { while (top ()) pop (); }; | |
59 | ||
60 | ~Doctree () { | |
61 | delete rootNode; | |
62 | }; | |
63 | ||
43 | 64 | DoctreeNode *push () { |
44 | 65 | DoctreeNode *dn = new DoctreeNode (); |
45 | 66 | dn->parent = topNode; |
67 | dn->sibling = dn->parent->lastChild; | |
68 | dn->parent->lastChild = dn; | |
46 | 69 | dn->num = num++; |
47 | 70 | topNode = dn; |
48 | 71 | return dn; |
49 | 72 | }; |
73 | ||
50 | 74 | void pop () { |
51 | DoctreeNode *dn = topNode; | |
52 | if (dn) { | |
53 | dFree ((void*) dn->id); | |
54 | if (dn->klass) { | |
55 | for (int i = 0; i < dn->klass->size (); i++) | |
56 | dFree (dn->klass->get(i)); | |
57 | delete dn->klass; | |
58 | } | |
59 | topNode = dn->parent; | |
60 | delete dn; | |
61 | } | |
75 | assert (topNode != rootNode); // never pop the root node | |
76 | topNode = topNode->parent; | |
62 | 77 | }; |
78 | ||
63 | 79 | inline DoctreeNode *top () { |
64 | return topNode; | |
80 | if (topNode != rootNode) | |
81 | return topNode; | |
82 | else | |
83 | return NULL; | |
65 | 84 | }; |
85 | ||
66 | 86 | inline DoctreeNode *parent (const DoctreeNode *node) { |
67 | return node->parent; | |
87 | if (node->parent != rootNode) | |
88 | return node->parent; | |
89 | else | |
90 | return NULL; | |
91 | }; | |
92 | ||
93 | inline DoctreeNode *sibling (const DoctreeNode *node) { | |
94 | return node->sibling; | |
68 | 95 | }; |
69 | 96 | }; |
70 | 97 |
40 | 40 | // Let these keys get to the UI |
41 | 41 | return 0; |
42 | 42 | } |
43 | } else if (modifier == FL_CTRL) { | |
44 | if (k == 'a' || k == 'e') { | |
45 | position(k == 'a' ? 0 : size()); | |
46 | return 1; | |
47 | } else if (k == 'k') { | |
48 | cut(position(), size()); | |
49 | return 1; | |
50 | } else if (k == 'd') { | |
51 | cut(position(), position()+1); | |
52 | return 1; | |
53 | } | |
43 | 54 | } else if (k == FL_Escape && modifier == 0) { |
44 | 55 | // Avoid clearing the text with Esc, just hide the findbar. |
45 | 56 | return 0; |
85 | 96 | */ |
86 | 97 | void Findbar::hide_cb(Fl_Widget *, void *vfb) |
87 | 98 | { |
88 | ((Findbar *)vfb)->hide(); | |
99 | a_UIcmd_findbar_toggle(a_UIcmd_get_bw_by_widget(vfb), 0); | |
89 | 100 | } |
90 | 101 | |
91 | 102 | /* |
163 | 174 | */ |
164 | 175 | int Findbar::handle(int event) |
165 | 176 | { |
166 | int ret = 0; | |
167 | 177 | int k = Fl::event_key(); |
168 | 178 | unsigned modifier = Fl::event_state() & (FL_SHIFT| FL_CTRL| FL_ALT|FL_META); |
169 | 179 | |
170 | 180 | if (event == FL_KEYBOARD && modifier == 0 && k == FL_Escape) { |
171 | hide(); | |
172 | ret = 1; | |
173 | } | |
174 | ||
175 | if (ret == 0) | |
176 | ret = Fl_Group::handle(event); | |
177 | ||
178 | return ret; | |
181 | /* let the UI handle it */ | |
182 | return 0; | |
183 | } | |
184 | ||
185 | return Fl_Group::handle(event); | |
179 | 186 | } |
180 | 187 | |
181 | 188 | /* |
187 | 194 | dReturn_if (bw == NULL); |
188 | 195 | |
189 | 196 | // It takes more than just calling show() to do the trick |
190 | //a_UIcmd_findbar_toggle(bw, 1); | |
191 | 197 | Fl_Group::show(); |
192 | 198 | |
193 | 199 | /* select text even if already focused */ |
195 | 201 | i->position(i->size(), 0); |
196 | 202 | } |
197 | 203 | |
198 | /* | |
199 | * Hide the findbar and reset the search state | |
200 | */ | |
201 | void Findbar::hide() | |
202 | { | |
203 | BrowserWindow *bw = a_UIcmd_get_bw_by_widget(this); | |
204 | dReturn_if (bw == NULL); | |
205 | ||
206 | // It takes more than just calling hide() to do the trick | |
207 | Fl_Group::hide(); | |
208 | a_UIcmd_findbar_toggle(bw, 0); | |
209 | ||
210 | a_UIcmd_findtext_reset(bw); | |
211 | a_UIcmd_focus_main_area(bw); | |
212 | } |
26 | 26 | ~Findbar(); |
27 | 27 | int handle(int event); |
28 | 28 | void show(); |
29 | void hide(); | |
30 | 29 | }; |
31 | 30 | |
32 | 31 | #endif // __FINDBAR_HH__ |
676 | 676 | case 2: /* End code... consume remaining data chunks..? */ |
677 | 677 | goto error; /* Could clean up better? */ |
678 | 678 | default: |
679 | printf("dillo_gif_decode: error!\n"); | |
679 | MSG("Gif_decode: error!\n"); | |
680 | 680 | goto error; |
681 | 681 | } |
682 | 682 | } |
646 | 646 | */ |
647 | 647 | void DilloHtml::loadImages (const DilloUrl *pattern) |
648 | 648 | { |
649 | dReturn_if_fail (bw->nav_expecting == FALSE); | |
649 | dReturn_if (a_Bw_expecting(bw)); | |
650 | 650 | |
651 | 651 | /* If the user asked for a specific URL, the user (NULL) is the requester, |
652 | 652 | * but if the user just asked for all URLs, use the page URL as the |
687 | 687 | if (link == -1) { |
688 | 688 | _MSG(" Link LEAVE notify...\n"); |
689 | 689 | a_UIcmd_set_msg(bw, ""); |
690 | a_UIcmd_set_pointer_on_link(bw, FALSE); | |
691 | 690 | } else { |
692 | 691 | _MSG(" Link ENTER notify...\n"); |
693 | 692 | Html_set_link_coordinates(html, link, x, y); |
694 | 693 | a_UIcmd_set_msg(bw, "%s", URL_STR(html->links->get(link))); |
695 | a_UIcmd_set_pointer_on_link(bw, TRUE); | |
696 | 694 | } |
697 | 695 | return true; |
698 | 696 | } |
100 | 100 | { "open" , KEYS_OPEN , FL_CTRL , 'o' }, |
101 | 101 | { "new-window" , KEYS_NEW_WINDOW , FL_CTRL , 'n' }, |
102 | 102 | { "new-tab" , KEYS_NEW_TAB , FL_CTRL , 't' }, |
103 | { "left-tab" , KEYS_LEFT_TAB , FL_SHIFT , FL_Tab }, | |
103 | { "left-tab" , KEYS_LEFT_TAB , FL_CTRL | | |
104 | FL_SHIFT , FL_Tab }, | |
104 | 105 | { "right-tab" , KEYS_RIGHT_TAB , FL_CTRL , FL_Tab }, |
105 | 106 | { "close-tab" , KEYS_CLOSE_TAB , FL_CTRL , 'w' }, |
106 | 107 | { "find" , KEYS_FIND , FL_CTRL , 'f' }, |
5 | 5 | # The commented-out bindings below show the defaults built into Dillo. |
6 | 6 | # |
7 | 7 | # Modifiers recognized: "Shift", "Ctrl", "Alt", "Meta". |
8 | # (OS X: Use "Meta" for Command) | |
8 | 9 | # |
9 | 10 | # Key names recognized: "Backspace", "Delete", "Down", "End", "Esc", |
10 | 11 | # "F1" through "F12", "Home", "Insert", "Left", "PageDown", "PageUp", |
37 | 38 | #<ctrl>q = close-all |
38 | 39 | |
39 | 40 | # "left-tab" and "right-tab" switch to the left/right of the current tab. |
40 | # <shift>tab = left-tab | |
41 | # <ctrl><shift>tab = left-tab | |
41 | 42 | # <ctrl>tab = right-tab |
42 | 43 | |
43 | 44 | # "back" and "forward" move back/forward through the browser history. |
630 | 630 | {"Panel size", 0, Menu_nop_cb, (void*)"Submenu1", FL_SUBMENU,0,0,0,0}, |
631 | 631 | {"tiny", 0,Menu_panel_change_cb,(void*)0,FL_MENU_RADIO,0,0,0,0}, |
632 | 632 | {"small", 0,Menu_panel_change_cb,(void*)1,FL_MENU_RADIO,0,0,0,0}, |
633 | {"medium",0,Menu_panel_change_cb,(void*)2,FL_MENU_RADIO,0,0,0,0}, | |
634 | {"large", 0,Menu_panel_change_cb,(void*)3, | |
633 | {"medium",0,Menu_panel_change_cb,(void*)2, | |
635 | 634 | FL_MENU_RADIO|FL_MENU_DIVIDER,0,0,0,0}, |
636 | 635 | {"small icons", 0,Menu_panel_change_cb,(void*)10, |
637 | 636 | FL_MENU_TOGGLE,0,0,0,0}, |
650 | 649 | if (prefs.load_images) |
651 | 650 | pm[2].set(); |
652 | 651 | pm[4+cur_panelsize].setonly(); |
653 | cur_smallicons ? pm[8].set() : pm[8].clear(); | |
652 | cur_smallicons ? pm[7].set() : pm[7].clear(); | |
654 | 653 | |
655 | 654 | item = pm->popup(wid->x(), wid->y() + wid->h()); |
656 | 655 | if (item) { |
410 | 410 | } |
411 | 411 | } |
412 | 412 | *label = buf; |
413 | if (ret == -1) | |
414 | MSG("Invalid search_url: \"%s\"\n", source); | |
413 | 415 | return ret; |
414 | 416 | } |
415 | 417 |
247 | 247 | */ |
248 | 248 | void a_Nav_cancel_expect(BrowserWindow *bw) |
249 | 249 | { |
250 | if (bw->nav_expecting) { | |
251 | a_Url_free(bw->nav_expect_url); | |
252 | bw->nav_expect_url = NULL; | |
253 | bw->nav_expecting = FALSE; | |
250 | if (a_Bw_expecting(bw)) { | |
251 | a_Bw_cancel_expect(bw); | |
254 | 252 | a_UIcmd_set_buttons_sens(bw); |
255 | 253 | } |
256 | 254 | if (bw->meta_refresh_status > 0) |
262 | 260 | */ |
263 | 261 | void a_Nav_cancel_expect_if_eq(BrowserWindow *bw, const DilloUrl *url) |
264 | 262 | { |
265 | if (bw->nav_expecting && a_Url_cmp(url, bw->nav_expect_url) == 0) | |
263 | if (a_Url_cmp(url, a_Bw_expected_url(bw)) == 0) | |
266 | 264 | a_Nav_cancel_expect(bw); |
267 | 265 | } |
268 | 266 | |
280 | 278 | |
281 | 279 | dReturn_if_fail(bw != NULL); |
282 | 280 | |
283 | if (bw->nav_expecting) { | |
284 | url = bw->nav_expect_url; | |
281 | if (a_Bw_expecting(bw)) { | |
282 | url = a_Url_dup(a_Bw_expected_url(bw)); | |
285 | 283 | reload = (URL_FLAGS(url) & URL_ReloadPage); |
286 | 284 | repush = (URL_FLAGS(url) & URL_ReloadFromCache); |
287 | 285 | e2equery = (URL_FLAGS(url) & URL_E2EQuery); |
292 | 290 | m = URL_E2EQuery|URL_ReloadPage|URL_ReloadFromCache|URL_IgnoreScroll; |
293 | 291 | a_Url_set_flags(url, URL_FLAGS(url) & ~m); |
294 | 292 | url_idx = a_History_add_url(url); |
293 | a_Url_free(url); | |
295 | 294 | |
296 | 295 | if (repush) { |
297 | 296 | MSG("a_Nav_expect_done: repush!\n"); |
344 | 343 | void a_Nav_push(BrowserWindow *bw, const DilloUrl *url, |
345 | 344 | const DilloUrl *requester) |
346 | 345 | { |
347 | dReturn_if_fail (bw != NULL); | |
348 | ||
349 | if (bw->nav_expecting && !a_Url_cmp(bw->nav_expect_url, url) && | |
350 | !strcmp(URL_FRAGMENT(bw->nav_expect_url),URL_FRAGMENT(url))) { | |
346 | const DilloUrl *e_url; | |
347 | dReturn_if_fail (bw != NULL); | |
348 | ||
349 | e_url = a_Bw_expected_url(bw); | |
350 | if (e_url && !a_Url_cmp(e_url, url) && | |
351 | !strcmp(URL_FRAGMENT(e_url),URL_FRAGMENT(url))) { | |
351 | 352 | /* we're already expecting that url (most probably a double-click) */ |
352 | 353 | return; |
353 | 354 | } |
354 | 355 | a_Nav_cancel_expect(bw); |
355 | bw->nav_expect_url = a_Url_dup(url); | |
356 | bw->nav_expecting = TRUE; | |
356 | a_Bw_expect(bw, url); | |
357 | 357 | Nav_open_url(bw, url, requester, 0); |
358 | 358 | } |
359 | 359 | |
369 | 369 | url = a_Url_dup(a_History_get_url(NAV_TOP_UIDX(bw))); |
370 | 370 | /* Let's make reload be from Cache */ |
371 | 371 | a_Url_set_flags(url, URL_FLAGS(url) | URL_ReloadFromCache); |
372 | bw->nav_expect_url = a_Url_dup(url); | |
373 | bw->nav_expecting = TRUE; | |
372 | a_Bw_expect(bw, url); | |
374 | 373 | Nav_open_url(bw, url, NULL, 0); |
375 | 374 | a_Url_free(url); |
376 | 375 | } |
500 | 499 | a_Url_set_flags(r_url, URL_FLAGS(r_url) | URL_E2EQuery); |
501 | 500 | /* This is an explicit reload, so clear the SpamSafe flag */ |
502 | 501 | a_Url_set_flags(r_url, URL_FLAGS(r_url) & ~URL_SpamSafe); |
503 | bw->nav_expect_url = r_url; | |
504 | bw->nav_expecting = TRUE; | |
502 | a_Bw_expect(bw, r_url); | |
505 | 503 | Nav_open_url(bw, r_url, NULL, 0); |
504 | a_Url_free(r_url); | |
506 | 505 | } |
507 | 506 | } |
508 | 507 | } |
17 | 17 | #define PREFS_FONT_CURSIVE "URW Chancery L" |
18 | 18 | #define PREFS_FONT_FANTASY "DejaVu Sans" /* TODO: find good default */ |
19 | 19 | #define PREFS_FONT_MONOSPACE "DejaVu Sans Mono" |
20 | #define PREFS_SEARCH_URL "http://www.google.com/search?ie=UTF-8&oe=UTF-8&q=%s" | |
20 | #define PREFS_SEARCH_URL "http://duckduckgo.com/lite/?kp=-1&q=%s" | |
21 | 21 | #define PREFS_NO_PROXY "localhost 127.0.0.1" |
22 | 22 | #define PREFS_SAVE_DIR "/tmp/" |
23 | 23 | #define PREFS_HTTP_REFERER "host" |
23 | 23 | #define PREFS_GEOMETRY_DEFAULT_YPOS -9999 |
24 | 24 | |
25 | 25 | /* Panel sizes */ |
26 | enum { P_tiny = 0, P_small, P_medium, P_large }; | |
26 | enum { P_tiny = 0, P_small, P_medium }; | |
27 | 27 | |
28 | 28 | enum {PREFS_FILTER_ALLOW_ALL, |
29 | 29 | PREFS_FILTER_SAME_DOMAIN}; |
175 | 175 | prefs.panel_size = P_tiny; |
176 | 176 | else if (!dStrcasecmp(value, "small")) |
177 | 177 | prefs.panel_size = P_small; |
178 | else if (!dStrcasecmp(value, "large")) | |
179 | prefs.panel_size = P_large; | |
180 | 178 | else /* default to "medium" */ |
181 | 179 | prefs.panel_size = P_medium; |
182 | 180 | break; |
153 | 153 | //---------------------------------------------------------------------------- |
154 | 154 | |
155 | 155 | /* |
156 | * A button that highlights on mouse over | |
157 | */ | |
158 | class CustLightButton : public Fl_Button { | |
159 | Fl_Color norm_color; | |
160 | public: | |
161 | CustLightButton(int x, int y, int w, int h, const char *l=0) : | |
162 | Fl_Button(x,y,w,h,l) { norm_color = color(); }; | |
163 | virtual int handle(int e); | |
164 | }; | |
165 | ||
166 | int CustLightButton::handle(int e) | |
167 | { | |
168 | if (active()) { | |
169 | if (e == FL_ENTER) { | |
170 | color(51); // {17,26,51} | |
171 | redraw(); | |
172 | } else if (e == FL_LEAVE || e == FL_RELEASE) { | |
173 | color(norm_color); | |
174 | redraw(); | |
175 | } | |
176 | } | |
177 | return Fl_Button::handle(e); | |
178 | } | |
179 | ||
180 | //---------------------------------------------------------------------------- | |
181 | ||
182 | /* | |
183 | 156 | * Used to handle "paste" within the toolbar's Clear button. |
184 | 157 | */ |
185 | 158 | class CustPasteButton : public CustLightButton { |
220 | 193 | padding = w > 2 ? w/2 : 1; |
221 | 194 | } |
222 | 195 | copy_label(lbl); |
223 | //measure_label(w,h); | |
224 | //size(w+padding,this->h()); | |
225 | 196 | } |
226 | 197 | }; |
227 | 198 | |
302 | 273 | } |
303 | 274 | |
304 | 275 | /* |
305 | * Change the color of the location bar. | |
306 | * | |
307 | static void color_change_cb(Fl_Widget *wid, void *data) | |
308 | { | |
309 | ((UI*)data)->color_change_cb_i(); | |
310 | } | |
311 | */ | |
312 | ||
313 | ||
314 | /* | |
315 | 276 | * Send the browser to the new URL in the location. |
316 | 277 | */ |
317 | 278 | static void location_cb(Fl_Widget *wid, void *data) |
322 | 283 | _MSG("location_cb()\n"); |
323 | 284 | a_UIcmd_open_urlstr(a_UIcmd_get_bw_by_widget(i), i->value()); |
324 | 285 | |
325 | if (ui->get_panelmode() == UI_TEMPORARILY_SHOW_PANELS) { | |
326 | ui->set_panelmode(UI_HIDDEN); | |
327 | } | |
286 | if (ui->temporaryPanels()) | |
287 | ui->panels_toggle(); | |
328 | 288 | } |
329 | 289 | |
330 | 290 | |
536 | 496 | padding = w; |
537 | 497 | btn->copy_label(PanelSize == P_tiny ? "&F" : "&File"); |
538 | 498 | btn->measure_label(w,h); |
539 | h = (PanelSize == P_large) ? mh : (PanelSize == P_tiny) ? bh : lh; | |
499 | h = (PanelSize == P_tiny) ? bh : lh; | |
540 | 500 | btn->size(w+padding, h); |
541 | 501 | p_xpos += btn->w(); |
542 | 502 | _MSG("UI::make_filemenu_button w=%d h=%d padding=%d\n", w, h, padding); |
543 | btn->box(PanelSize == P_large ? FL_THIN_UP_BOX : FL_THIN_UP_BOX); | |
503 | btn->box(FL_THIN_UP_BOX); | |
544 | 504 | btn->callback(filemenu_cb, this); |
545 | 505 | if (prefs.show_tooltip) |
546 | 506 | btn->tooltip("File menu"); |
580 | 540 | bw = 42, bh = 36, mh = 0, lh = 22, lbl = 1; |
581 | 541 | else |
582 | 542 | bw = 45, bh = 45, mh = 0, lh = 28, lbl = 1; |
583 | } else { // P_large | |
584 | if (Small_Icons) | |
585 | bw = 42, bh = 36, mh = 22, lh = 22, lbl = 1; | |
586 | else | |
587 | bw = 45, bh = 45, mh = 24, lh = 28, lbl = 1; | |
588 | 543 | } |
589 | 544 | nh = bh, fh = 28; sh = 20; |
590 | 545 | |
603 | 558 | NavBar->rearrange(); |
604 | 559 | TopGroup->insert(*NavBar,0); |
605 | 560 | } else { |
606 | if (PanelSize == P_large) { | |
607 | MenuBar = new CustGroupHorizontal(0,0,ww,mh); | |
608 | MenuBar->begin(); | |
609 | MenuBar->box(FL_THIN_UP_BOX); | |
610 | Fl_Widget *bn = make_filemenu_button(); | |
611 | MenuBar->add_resizable(*new Fl_Box(bn->w(),0,ww - bn->w(),mh)); | |
612 | MenuBar->end(); | |
613 | MenuBar->rearrange(); | |
614 | TopGroup->insert(*MenuBar,0); | |
615 | ||
616 | p_xpos = 0; | |
617 | LocBar = new CustGroupHorizontal(0,0,ww,lh); | |
618 | LocBar->begin(); | |
619 | make_location(ww); | |
620 | LocBar->resizable(Location); | |
621 | LocBar->end(); | |
622 | LocBar->rearrange(); | |
623 | TopGroup->insert(*LocBar,1); | |
624 | } else { | |
625 | LocBar = new CustGroupHorizontal(0,0,ww,lh); | |
626 | LocBar->box(FL_NO_BOX); | |
627 | LocBar->begin(); | |
628 | p_xpos = 0; | |
629 | make_filemenu_button(); | |
630 | make_location(ww); | |
631 | LocBar->resizable(Location); | |
632 | LocBar->end(); | |
633 | LocBar->rearrange(); | |
634 | TopGroup->insert(*LocBar,0); | |
635 | } | |
561 | // Location | |
562 | LocBar = new CustGroupHorizontal(0,0,ww,lh); | |
563 | LocBar->box(FL_NO_BOX); | |
564 | LocBar->begin(); | |
565 | p_xpos = 0; | |
566 | make_filemenu_button(); | |
567 | make_location(ww); | |
568 | LocBar->resizable(Location); | |
569 | LocBar->end(); | |
570 | LocBar->rearrange(); | |
571 | TopGroup->insert(*LocBar,0); | |
636 | 572 | |
637 | 573 | // Toolbar |
638 | 574 | p_ypos = 0; |
651 | 587 | } |
652 | 588 | NavBar->end(); |
653 | 589 | NavBar->rearrange(); |
654 | TopGroup->insert(*NavBar,(MenuBar ? 2 : 1)); | |
590 | TopGroup->insert(*NavBar,1); | |
655 | 591 | } |
656 | 592 | } |
657 | 593 | |
693 | 629 | UI::UI(int x, int y, int ui_w, int ui_h, const char* label, const UI *cur_ui) : |
694 | 630 | CustGroupVertical(x, y, ui_w, ui_h, label) |
695 | 631 | { |
696 | PointerOnLink = FALSE; | |
697 | ||
698 | MenuBar = LocBar = NavBar = StatusBar = NULL; | |
632 | LocBar = NavBar = StatusBar = NULL; | |
699 | 633 | |
700 | 634 | Tabs = NULL; |
701 | 635 | TabTooltip = NULL; |
703 | 637 | TopGroup->box(FL_NO_BOX); |
704 | 638 | clear_flag(SHORTCUT_LABEL); |
705 | 639 | |
640 | PanelTemporary = false; | |
706 | 641 | if (cur_ui) { |
707 | 642 | PanelSize = cur_ui->PanelSize; |
708 | 643 | CuteColor = cur_ui->CuteColor; |
709 | 644 | Small_Icons = cur_ui->Small_Icons; |
710 | if (cur_ui->Panelmode == UI_HIDDEN || | |
711 | cur_ui->Panelmode == UI_TEMPORARILY_SHOW_PANELS) | |
712 | Panelmode = UI_HIDDEN; | |
713 | else | |
714 | Panelmode = UI_NORMAL; | |
645 | Panelmode = cur_ui->Panelmode; | |
715 | 646 | } else { |
716 | 647 | // Set some default values |
717 | //PanelSize = P_tiny, CuteColor = 26, Small_Icons = 0; | |
718 | 648 | PanelSize = prefs.panel_size; |
719 | 649 | Small_Icons = prefs.small_icons; |
720 | 650 | CuteColor = 206; |
739 | 669 | MainIdx = TopGroup->find(Main); |
740 | 670 | |
741 | 671 | // Find text bar |
742 | FindBarSpace = 1; | |
743 | 672 | FindBar = new Findbar(ui_w, fh); |
744 | 673 | TopGroup->add(FindBar); |
745 | 674 | |
749 | 678 | TopGroup->end(); |
750 | 679 | TopGroup->rearrange(); |
751 | 680 | |
752 | // Make the full screen button (to be attached to the viewport later) | |
753 | // TODO: attach to the viewport | |
754 | //FullScreen = new Fl_Button(0,0,15,15); | |
755 | //FullScreen->image(ImgFullScreenOn); | |
756 | //FullScreen->tooltip("Hide Controls"); | |
757 | //FullScreen->callback(fullscreen_cb, this); | |
758 | ||
759 | 681 | customize(0); |
760 | 682 | |
761 | 683 | if (Panelmode == UI_HIDDEN) { |
770 | 692 | { |
771 | 693 | _MSG("UI::~UI()\n"); |
772 | 694 | dFree(TabTooltip); |
773 | ||
774 | if (!FindBarSpace) | |
775 | delete FindBar; | |
776 | 695 | } |
777 | 696 | |
778 | 697 | /* |
787 | 706 | /* WORKAROUND: remove the Panel's fltk-tooltip. |
788 | 707 | * Although the expose event is delivered, it has an offset. This |
789 | 708 | * extra call avoids the lingering tooltip. */ |
790 | if (Fl::event_inside(NavBar) || | |
791 | (LocBar && Fl::event_inside(LocBar)) || | |
792 | (MenuBar && Fl::event_inside(MenuBar)) || | |
793 | (StatusBar && Fl::event_inside(StatusBar))) | |
709 | if (!Fl::event_inside(Main) && | |
710 | (Fl::event_inside((Fl_Widget*)tabs()) || | |
711 | Fl::event_inside(NavBar) || | |
712 | (LocBar && Fl::event_inside(LocBar)) || | |
713 | (StatusBar && Fl::event_inside(StatusBar)))) | |
794 | 714 | window()->damage(FL_DAMAGE_EXPOSE,0,0,1,1); |
795 | 715 | |
796 | 716 | return 0; // Receive as shortcut |
820 | 740 | a_UIcmd_search_dialog(a_UIcmd_get_bw_by_widget(this)); |
821 | 741 | ret = 1; |
822 | 742 | } else if (cmd == KEYS_GOTO) { |
743 | if (Panelmode == UI_HIDDEN) { | |
744 | panels_toggle(); | |
745 | temporaryPanels(true); | |
746 | } | |
823 | 747 | focus_location(); |
824 | 748 | ret = 1; |
825 | 749 | } else if (cmd == KEYS_HIDE_PANELS) { |
826 | 750 | /* Hide findbar if present, hide panels if not */ |
827 | (FindBarSpace) ? findbar_toggle(0) : panels_toggle(); | |
828 | ret = 1; | |
829 | //if (get_panelmode() == UI_TEMPORARILY_SHOW_PANELS) | |
830 | // set_panelmode(UI_HIDDEN); | |
751 | (FindBar->visible()) ? findbar_toggle(0) : panels_toggle(); | |
752 | temporaryPanels(false); | |
753 | ret = 1; | |
831 | 754 | } else if (cmd == KEYS_OPEN) { |
832 | 755 | a_UIcmd_open_file(a_UIcmd_get_bw_by_widget(this)); |
833 | 756 | ret = 1; |
897 | 820 | */ |
898 | 821 | void UI::focus_location() |
899 | 822 | { |
900 | if (get_panelmode() == UI_HIDDEN) { | |
901 | // Temporary panel handling is disabled now. | |
902 | //set_panelmode(UI_TEMPORARILY_SHOW_PANELS); | |
903 | } | |
904 | 823 | Location->take_focus(); |
905 | 824 | // Make text selected when already focused. |
906 | 825 | Location->position(Location->size(), 0); |
1037 | 956 | { |
1038 | 957 | // Remove current panel's bars |
1039 | 958 | init_sizes(); |
1040 | TopGroup->remove(MenuBar); | |
1041 | Fl::delete_widget(MenuBar); | |
1042 | 959 | TopGroup->remove(LocBar); |
1043 | 960 | Fl::delete_widget(LocBar); |
1044 | 961 | TopGroup->remove(NavBar); |
1045 | 962 | Fl::delete_widget(NavBar); |
1046 | MenuBar = LocBar = NavBar = NULL; | |
963 | LocBar = NavBar = NULL; | |
1047 | 964 | |
1048 | 965 | // Set internal vars for panel size |
1049 | 966 | PanelSize = new_size; |
1074 | 991 | } |
1075 | 992 | |
1076 | 993 | /* |
1077 | * Set or remove the Panelmode flag and update the UI accordingly | |
1078 | */ | |
1079 | void UI::set_panelmode(UIPanelmode mode) | |
1080 | { | |
1081 | if (mode == UI_HIDDEN) { | |
1082 | //Panel->hide(); | |
1083 | StatusBar->hide(); | |
1084 | } else { | |
1085 | /* UI_NORMAL or UI_TEMPORARILY_SHOW_PANELS */ | |
1086 | //Panel->show(); | |
1087 | StatusBar->show(); | |
1088 | } | |
1089 | Panelmode = mode; | |
1090 | TopGroup->rearrange(); | |
1091 | } | |
1092 | ||
1093 | /* | |
1094 | * Get the value of the panelmode flag | |
1095 | */ | |
1096 | UIPanelmode UI::get_panelmode() | |
1097 | { | |
1098 | return Panelmode; | |
1099 | } | |
1100 | ||
1101 | /* | |
1102 | * Toggle the Control Panel out of the way | |
1103 | */ | |
1104 | void UI::panelmode_cb_i() | |
1105 | { | |
1106 | set_panelmode((UIPanelmode) !Panelmode); | |
1107 | } | |
1108 | ||
1109 | /* | |
1110 | 994 | * Set 'nw' as the main render area widget |
1111 | 995 | */ |
1112 | 996 | void UI::set_render_layout(Fl_Group *nw) |
1129 | 1013 | switch (btn) { |
1130 | 1014 | case UI_BACK: |
1131 | 1015 | (sens) ? Back->activate() : Back->deactivate(); |
1132 | // Back->redraw(DAMAGE_HIGHLIGHT); | |
1133 | 1016 | break; |
1134 | 1017 | case UI_FORW: |
1135 | 1018 | (sens) ? Forw->activate() : Forw->deactivate(); |
1136 | // Forw->redraw(DAMAGE_HIGHLIGHT); | |
1137 | 1019 | break; |
1138 | 1020 | case UI_STOP: |
1139 | 1021 | (sens) ? Stop->activate() : Stop->deactivate(); |
1140 | // Stop->redraw(DAMAGE_HIGHLIGHT); | |
1141 | 1022 | break; |
1142 | 1023 | default: |
1143 | 1024 | break; |
1159 | 1040 | { |
1160 | 1041 | /* WORKAROUND: |
1161 | 1042 | * This is tricky: As fltk-1.3 resizes hidden widgets (which it |
1162 | * doesn't resize when visible!). We need to go through hoops to | |
1043 | * doesn't resize when visible!). We need to set the size to (0,0) to | |
1163 | 1044 | * get the desired behaviour. |
1164 | 1045 | * (STR#2639 in FLTK bug tracker). |
1165 | 1046 | */ |
1166 | 1047 | |
1167 | 1048 | if (add) { |
1168 | if (!FindBarSpace) { | |
1169 | // show | |
1170 | Main->size(Main->w(), Main->h()-FindBar->h()); | |
1171 | insert(*FindBar, StatusBar); | |
1172 | FindBar->show(); | |
1173 | FindBarSpace = 1; | |
1174 | } else { | |
1175 | // select text | |
1176 | FindBar->show(); | |
1177 | } | |
1178 | } else if (!add && FindBarSpace) { | |
1049 | if (!FindBar->visible()) | |
1050 | FindBar->size(w(), fh); | |
1051 | FindBar->show(); | |
1052 | } else { | |
1179 | 1053 | // hide |
1180 | Main->size(Main->w(), Main->h()+FindBar->h()); | |
1181 | remove(FindBar); | |
1182 | FindBarSpace = 0; | |
1054 | FindBar->size(0,0); | |
1055 | FindBar->hide(); | |
1056 | // reset state | |
1057 | a_UIcmd_findtext_reset(a_UIcmd_get_bw_by_widget(this)); | |
1058 | // focus main area | |
1059 | focus_main(); | |
1183 | 1060 | } |
1184 | 1061 | TopGroup->rearrange(); |
1185 | 1062 | } |
1196 | 1073 | |
1197 | 1074 | // hide/show panels |
1198 | 1075 | init_sizes(); |
1199 | if (MenuBar) { | |
1200 | hide ? MenuBar->size(0,0) : MenuBar->size(w(),mh); | |
1201 | hide ? MenuBar->hide() : MenuBar->show(); | |
1202 | } | |
1203 | 1076 | if (LocBar) { |
1204 | 1077 | hide ? LocBar->size(0,0) : LocBar->size(w(),lh); |
1205 | 1078 | hide ? LocBar->hide() : LocBar->show(); |
1215 | 1088 | } |
1216 | 1089 | |
1217 | 1090 | TopGroup->rearrange(); |
1218 | } | |
1091 | Panelmode = (hide) ? UI_HIDDEN : UI_NORMAL; | |
1092 | } |
27 | 27 | |
28 | 28 | typedef enum { |
29 | 29 | UI_NORMAL = 0, /* make sure it's compatible with bool */ |
30 | UI_HIDDEN = 1, | |
31 | UI_TEMPORARILY_SHOW_PANELS | |
30 | UI_HIDDEN = 1 | |
32 | 31 | } UIPanelmode; |
32 | ||
33 | ||
34 | // Min size to fit the full UI | |
35 | #define UI_MIN_W 600 | |
36 | #define UI_MIN_H 200 | |
33 | 37 | |
34 | 38 | // Private classes |
35 | 39 | class CustProgressBox; |
36 | 40 | class CustTabs; |
37 | 41 | |
38 | 42 | |
39 | // Class definition ---------------------------------------------------------- | |
43 | // Class definitions --------------------------------------------------------- | |
40 | 44 | /* |
41 | 45 | * Used to reposition group's widgets when some of them are hidden. |
42 | 46 | * All children get the height of the group but retain their original width. |
43 | 47 | * The resizable child get's the remaining space. |
44 | 48 | */ |
45 | 49 | class CustGroupHorizontal : public Fl_Group { |
50 | Fl_Widget *rsz; | |
46 | 51 | public: |
47 | 52 | CustGroupHorizontal(int x,int y,int w ,int h,const char *l = 0) : |
48 | 53 | Fl_Group(x,y,w,h,l) { }; |
52 | 57 | int sum = 0, _x = x(); |
53 | 58 | int children_ = children(); |
54 | 59 | |
60 | if (resizable()) | |
61 | rsz = resizable(); | |
62 | ||
55 | 63 | for (int i=0; i < children_; i++) |
56 | 64 | if (a[i] != resizable() && a[i]->visible()) |
57 | 65 | sum += a[i]->w(); |
58 | 66 | |
59 | 67 | for (int i=0; i < children_; i++) { |
60 | if (a[i] == resizable()) { | |
61 | a[i]->resize(_x, y(), w() - sum, h()); | |
68 | if (a[i] == rsz) { | |
69 | if (w() > sum) { | |
70 | a[i]->resize(_x, y(), w()-sum, h()); | |
71 | if (!resizable()) | |
72 | resizable(rsz); | |
73 | } else { | |
74 | /* widgets overflow width */ | |
75 | a[i]->resize(_x, y(), 0, h()); | |
76 | resizable(NULL); | |
77 | } | |
62 | 78 | } else { |
63 | 79 | a[i]->resize(_x, y(), a[i]->w(), h()); |
64 | 80 | } |
98 | 114 | } |
99 | 115 | }; |
100 | 116 | |
117 | /* | |
118 | * A button that highlights on mouse over | |
119 | */ | |
120 | class CustLightButton : public Fl_Button { | |
121 | Fl_Color norm_color, light_color; | |
122 | public: | |
123 | CustLightButton(int x, int y, int w, int h, const char *l=0) : | |
124 | Fl_Button(x,y,w,h,l) { norm_color = color(); light_color = 51; }; | |
125 | virtual int handle(int e) | |
126 | { | |
127 | if (active()) { | |
128 | if (e == FL_ENTER) { | |
129 | color(light_color); // {17,26,51} | |
130 | redraw(); | |
131 | } else if (e == FL_LEAVE || e == FL_RELEASE || e == FL_HIDE) { | |
132 | color(norm_color); | |
133 | redraw(); | |
134 | } | |
135 | } else if (e == FL_DEACTIVATE && color() != norm_color) { | |
136 | color(norm_color); | |
137 | redraw(); | |
138 | } | |
139 | return Fl_Button::handle(e); | |
140 | } | |
141 | void hl_color(Fl_Color col) { light_color = col; }; | |
142 | }; | |
101 | 143 | |
102 | 144 | // |
103 | 145 | // UI class definition ------------------------------------------------------- |
108 | 150 | |
109 | 151 | CustGroupVertical *TopGroup; |
110 | 152 | Fl_Button *Back, *Forw, *Home, *Reload, *Save, *Stop, *Bookmarks, *Tools, |
111 | *Clear, *Search, *Help, *FullScreen, *BugMeter, *FileButton; | |
112 | CustGroupHorizontal *MenuBar, *LocBar, *NavBar, *StatusBar; | |
153 | *Clear, *Search, *Help, *BugMeter, *FileButton; | |
154 | CustGroupHorizontal *LocBar, *NavBar, *StatusBar; | |
113 | 155 | Fl_Input *Location; |
114 | 156 | CustProgressBox *PProg, *IProg; |
115 | 157 | Fl_Group *Panel, *Main; |
116 | 158 | Fl_Output *StatusOutput; |
117 | 159 | Findbar *FindBar; |
118 | 160 | |
119 | int FindBarSpace, MainIdx; | |
161 | int MainIdx; | |
120 | 162 | // Panel customization variables |
121 | 163 | int PanelSize, CuteColor, Small_Icons; |
122 | 164 | int p_xpos, p_ypos, bw, bh, mh, lh, nh, fh, sh, pw, lbl; |
165 | bool PanelTemporary; | |
123 | 166 | |
124 | 167 | UIPanelmode Panelmode; |
125 | int PointerOnLink; | |
126 | 168 | Fl_Button *make_button(const char *label, Fl_Image *img, |
127 | 169 | Fl_Image*deimg, int b_n, int start = 0); |
128 | 170 | void make_toolbar(int tw, int th); |
153 | 195 | void customize(int flags); |
154 | 196 | void button_set_sens(UIButton btn, int sens); |
155 | 197 | void paste_url(); |
156 | void set_panelmode(UIPanelmode mode); | |
157 | UIPanelmode get_panelmode(); | |
158 | 198 | int get_panelsize() { return PanelSize; } |
159 | 199 | int get_smallicons() { return Small_Icons; } |
160 | 200 | void change_panel(int new_size, int small_icons); |
163 | 203 | |
164 | 204 | CustTabs *tabs() { return Tabs; } |
165 | 205 | void tabs(CustTabs *tabs) { Tabs = tabs; } |
166 | int pointerOnLink() { return PointerOnLink; } | |
167 | void pointerOnLink(int flag) { PointerOnLink = flag; } | |
206 | bool temporaryPanels() { return PanelTemporary; } | |
207 | void temporaryPanels(bool val) { PanelTemporary = val; } | |
168 | 208 | |
169 | 209 | // Hooks to method callbacks |
170 | 210 | void color_change_cb_i(); |
171 | 211 | void toggle_cb_i(); |
172 | void panelmode_cb_i(); | |
173 | 212 | }; |
174 | 213 | |
175 | 214 | #endif // __UI_HH__ |
0 | 0 | /* |
1 | 1 | * File: uicmd.cc |
2 | 2 | * |
3 | * Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org> | |
3 | * Copyright (C) 2005-2011 Jorge Arellano Cid <jcid@dillo.org> | |
4 | 4 | * |
5 | 5 | * This program is free software; you can redistribute it and/or modify |
6 | 6 | * it under the terms of the GNU General Public License as published by |
20 | 20 | #include <FL/Fl_Double_Window.H> |
21 | 21 | #include <FL/Fl_Wizard.H> |
22 | 22 | #include <FL/Fl_Box.H> |
23 | #include <FL/Fl_Pack.H> | |
24 | #include <FL/Fl_Scroll.H> | |
23 | 25 | #include <FL/names.h> |
24 | 26 | |
25 | 27 | #include "paths.hh" |
41 | 43 | |
42 | 44 | #include "nav.h" |
43 | 45 | |
44 | #define DEFAULT_TAB_LABEL "Dillo" | |
46 | //#define DEFAULT_TAB_LABEL "-.untitled.-" | |
47 | #define DEFAULT_TAB_LABEL "-.new.-" | |
45 | 48 | |
46 | 49 | // Handy macro |
47 | 50 | #define BW2UI(bw) ((UI*)((bw)->ui)) |
61 | 64 | * Forward declarations |
62 | 65 | */ |
63 | 66 | static BrowserWindow *UIcmd_tab_new(CustTabs *tabs, UI *old_ui, int focus); |
67 | static void close_tab_btn_cb (Fl_Widget *w, void *cb_data); | |
64 | 68 | |
65 | 69 | //---------------------------------------------------------------------------- |
66 | 70 | |
83 | 87 | /* |
84 | 88 | * Allows fine control of the tabbed interface |
85 | 89 | */ |
86 | class CustTabs : public CustGroupHorizontal { | |
87 | int tab_w, tab_h, ctab_h, tab_n; | |
90 | class CustTabs : public Fl_Group { | |
91 | int tab_w, tab_h, ctab_h, btn_w, ctl_w; | |
88 | 92 | Fl_Wizard *Wizard; |
89 | int tabcolor_inactive, tabcolor_active, curtab_idx; | |
93 | Fl_Scroll *Scroll; | |
94 | Fl_Pack *Pack; | |
95 | Fl_Group *Control; | |
96 | CustLightButton *CloseBtn; | |
97 | int tabcolor_inactive, tabcolor_active; | |
98 | ||
99 | void update_pack_offset(void); | |
100 | void resize(int x, int y, int w, int h) | |
101 | { Fl_Group::resize(x,y,w,h); update_pack_offset(); } | |
102 | int get_btn_idx(UI *ui); | |
103 | ||
90 | 104 | public: |
91 | 105 | CustTabs (int ww, int wh, int th, const char *lbl=0) : |
92 | CustGroupHorizontal(0,0,ww,th,lbl) { | |
93 | tab_w = 80, tab_h = th, ctab_h = 1, tab_n = 0, curtab_idx = -1; | |
106 | Fl_Group(0,0,ww,th,lbl) { | |
107 | Pack = NULL; | |
108 | tab_w = 50, tab_h = th, ctab_h = 1, btn_w = 20, ctl_w = 1*btn_w+2; | |
94 | 109 | tabcolor_active = FL_DARK_CYAN; tabcolor_inactive = 206; |
95 | 110 | resize(0,0,ww,ctab_h); |
96 | resizable(NULL); | |
111 | /* tab buttons go inside a pack within a scroll */ | |
112 | Scroll = new Fl_Scroll(0,0,ww-ctl_w,ctab_h); | |
113 | Scroll->type(0); /* no scrollbars */ | |
114 | Scroll->box(FL_NO_BOX); | |
115 | Pack = new Fl_Pack(0,0,ww-ctl_w,tab_h); | |
116 | Pack->type(Fl_Pack::HORIZONTAL); | |
117 | Pack->box(FL_NO_BOX); //FL_THIN_DOWN_FRAME | |
118 | Pack->end(); | |
119 | Scroll->end(); | |
120 | resizable(Scroll); | |
121 | ||
122 | /* control buttons go inside a group */ | |
123 | Control = new Fl_Group(ww-ctl_w,0,ctl_w,ctab_h); | |
124 | CloseBtn = new CustLightButton(ww-ctl_w+2,0,btn_w,ctab_h, "X"); | |
125 | CloseBtn->box(FL_PLASTIC_ROUND_UP_BOX); | |
126 | CloseBtn->labelcolor(0x00641000); | |
127 | CloseBtn->hl_color(FL_WHITE); | |
128 | CloseBtn->clear_visible_focus(); | |
129 | CloseBtn->tooltip(prefs.right_click_closes_tab ? | |
130 | "Close current tab.\nor Right-click tab label to close." : | |
131 | "Close current tab.\nor Middle-click tab label to close."); | |
132 | CloseBtn->callback(close_tab_btn_cb, this); | |
133 | CloseBtn->hide(); | |
134 | Control->end(); | |
135 | ||
97 | 136 | box(FL_FLAT_BOX); |
98 | 137 | end(); |
99 | 138 | |
105 | 144 | UI *add_new_tab(UI *old_ui, int focus); |
106 | 145 | void remove_tab(UI *ui); |
107 | 146 | Fl_Wizard *wizard(void) { return Wizard; } |
108 | int get_btn_idx(UI *ui); | |
109 | int num_tabs() { return children(); } | |
147 | int num_tabs() { return (Pack ? Pack->children() : 0); } | |
110 | 148 | void switch_tab(CustTabButton *cbtn); |
111 | 149 | void prev_tab(void); |
112 | 150 | void next_tab(void); |
113 | ||
114 | 151 | void set_tab_label(UI *ui, const char *title); |
115 | 152 | }; |
116 | 153 | |
129 | 166 | (b == FL_MIDDLE_MOUSE && !prefs.right_click_closes_tab)) { |
130 | 167 | // TODO: just an example, not necessarily final |
131 | 168 | a_UIcmd_close_bw(a_UIcmd_get_bw_by_widget(btn->ui())); |
169 | } | |
170 | } | |
171 | ||
172 | /* | |
173 | * Callback for the close-tab button | |
174 | */ | |
175 | static void close_tab_btn_cb (Fl_Widget *, void *cb_data) | |
176 | { | |
177 | CustTabs *tabs = (CustTabs*) cb_data; | |
178 | int b = Fl::event_button(); | |
179 | ||
180 | if (b == FL_LEFT_MOUSE) { | |
181 | a_UIcmd_close_bw(a_UIcmd_get_bw_by_widget(tabs->wizard()->value())); | |
132 | 182 | } |
133 | 183 | } |
134 | 184 | |
143 | 193 | UI *ui = (UI*)wizard()->value(); |
144 | 194 | BrowserWindow *bw = a_UIcmd_get_bw_by_widget(ui); |
145 | 195 | KeysCommand_t cmd = Keys::getKeyCmd(); |
146 | if (Fl::event_key() == FL_Escape) { | |
147 | // Hide findbar if present | |
148 | ui->findbar_toggle(0); | |
149 | ret = 1; | |
150 | } else if (cmd == KEYS_NOP) { | |
196 | if (cmd == KEYS_NOP) { | |
151 | 197 | // Do nothing |
152 | 198 | _MSG("CustTabs::handle KEYS_NOP\n"); |
153 | 199 | } else if (cmd == KEYS_NEW_TAB) { |
171 | 217 | } |
172 | 218 | } |
173 | 219 | |
174 | return (ret) ? ret : CustGroupHorizontal::handle(e); | |
220 | return (ret) ? ret : Fl_Group::handle(e); | |
175 | 221 | } |
176 | 222 | |
177 | 223 | /* |
179 | 225 | */ |
180 | 226 | UI *CustTabs::add_new_tab(UI *old_ui, int focus) |
181 | 227 | { |
182 | char tab_label[64]; | |
183 | ||
184 | 228 | if (num_tabs() == 1) { |
185 | 229 | // Show tabbar |
186 | 230 | ctab_h = tab_h; |
187 | 231 | Wizard->resize(0,ctab_h,Wizard->w(),window()->h()-ctab_h); |
188 | 232 | resize(0,0,window()->w(),ctab_h); // tabbar |
189 | child(0)->show(); // first tab button | |
233 | CloseBtn->show(); | |
234 | {int w = 0, h; Pack->child(0)->measure_label(w, h); | |
235 | Pack->child(0)->size(w+14,ctab_h);} | |
236 | Pack->child(0)->show(); // first tab button | |
190 | 237 | window()->init_sizes(); |
191 | 238 | } |
192 | 239 | |
240 | /* The UI is constructed in a comfortable fitting size, and then resized | |
241 | * so FLTK doesn't get confused later with even smaller dimensions! */ | |
193 | 242 | current(0); |
194 | UI *new_ui = new UI(0,ctab_h,Wizard->w(),Wizard->h(),0,old_ui); | |
243 | UI *new_ui = new UI(0,0,UI_MIN_W,UI_MIN_H,"Dillo:",old_ui); | |
244 | new_ui->resize(0,ctab_h,Wizard->w(),Wizard->h()); | |
195 | 245 | new_ui->tabs(this); |
196 | 246 | Wizard->add(new_ui); |
197 | 247 | new_ui->show(); |
198 | 248 | |
199 | snprintf(tab_label, 64,"ctab%d", ++tab_n); | |
200 | 249 | CustTabButton *btn = new CustTabButton(num_tabs()*tab_w,0,tab_w,ctab_h); |
201 | btn->align(FL_ALIGN_INSIDE|FL_ALIGN_CLIP); | |
202 | btn->copy_label(tab_label); | |
250 | btn->align(FL_ALIGN_INSIDE); | |
251 | btn->labelsize(btn->labelsize()-2); | |
252 | btn->copy_label(DEFAULT_TAB_LABEL); | |
203 | 253 | btn->clear_visible_focus(); |
204 | 254 | btn->box(FL_PLASTIC_ROUND_UP_BOX); |
205 | 255 | btn->color(focus ? tabcolor_active : tabcolor_inactive); |
206 | 256 | btn->ui(new_ui); |
207 | add(btn); | |
208 | 257 | btn->callback(tab_btn_cb, this); |
258 | Pack->add(btn); // append | |
209 | 259 | |
210 | 260 | if (focus) { |
211 | 261 | switch_tab(btn); |
215 | 265 | } |
216 | 266 | if (num_tabs() == 1) |
217 | 267 | btn->hide(); |
218 | rearrange(); | |
268 | update_pack_offset(); | |
219 | 269 | |
220 | 270 | return new_ui; |
221 | 271 | } |
231 | 281 | int act_idx = get_btn_idx((UI*)Wizard->value()); |
232 | 282 | // get to-be-removed tab idx |
233 | 283 | int rm_idx = get_btn_idx(ui); |
234 | btn = (CustTabButton*)child(rm_idx); | |
284 | btn = (CustTabButton*)Pack->child(rm_idx); | |
235 | 285 | |
236 | 286 | if (act_idx == rm_idx) { |
237 | 287 | // Active tab is being closed, switch to another one |
238 | 288 | rm_idx > 0 ? prev_tab() : next_tab(); |
239 | 289 | } |
240 | remove(rm_idx); | |
290 | Pack->remove(rm_idx); | |
291 | update_pack_offset(); | |
241 | 292 | delete btn; |
242 | rearrange(); | |
243 | 293 | |
244 | 294 | Wizard->remove(ui); |
245 | 295 | delete(ui); |
246 | 296 | |
247 | if (num_tabs() == 0) { | |
248 | window()->hide(); | |
249 | // TODO: free memory | |
250 | //delete window(); | |
251 | ||
252 | } else if (num_tabs() == 1) { | |
297 | if (num_tabs() == 1) { | |
253 | 298 | // hide tabbar |
254 | 299 | ctab_h = 1; |
255 | child(0)->hide(); // first tab button | |
300 | CloseBtn->hide(); | |
301 | Pack->child(0)->size(0,0); | |
302 | Pack->child(0)->hide(); // first tab button | |
256 | 303 | resize(0,0,window()->w(),ctab_h); // tabbar |
257 | 304 | Wizard->resize(0,ctab_h,Wizard->w(),window()->h()-ctab_h); |
258 | 305 | window()->init_sizes(); |
263 | 310 | int CustTabs::get_btn_idx(UI *ui) |
264 | 311 | { |
265 | 312 | for (int i = 0; i < num_tabs(); ++i) { |
266 | CustTabButton *btn = (CustTabButton*)child(i); | |
313 | CustTabButton *btn = (CustTabButton*)Pack->child(i); | |
267 | 314 | if (btn->ui() == ui) |
268 | 315 | return i; |
269 | 316 | } |
270 | 317 | return -1; |
318 | } | |
319 | ||
320 | /* | |
321 | * Keep active tab visible | |
322 | * (Pack children have unusable x() coordinate) | |
323 | */ | |
324 | void CustTabs::update_pack_offset() | |
325 | { | |
326 | dReturn_if (num_tabs() == 0); | |
327 | ||
328 | // get active tab button | |
329 | int act_idx = get_btn_idx((UI*)Wizard->value()); | |
330 | CustTabButton *cbtn = (CustTabButton*)Pack->child(act_idx); | |
331 | ||
332 | // calculate tab button's x() coordinates | |
333 | int x_i = 0, x_f; | |
334 | for (int j=0; j < act_idx; ++j) | |
335 | x_i += Pack->child(j)->w(); | |
336 | x_f = x_i + cbtn->w(); | |
337 | ||
338 | int scr_x = Scroll->xposition(), scr_y = Scroll->yposition(); | |
339 | int px_i = x_i - scr_x; | |
340 | int px_f = px_i + cbtn->w(); | |
341 | int pw = Scroll->window()->w() - ctl_w; | |
342 | _MSG(" scr_x=%d btn_x=%d px_i=%d btn_w=%d px_f=%d pw=%d", | |
343 | Scroll->xposition(),cbtn->x(),px_i,cbtn->w(),px_f,pw); | |
344 | if (px_i < 0) { | |
345 | Scroll->scroll_to(x_i, scr_y); | |
346 | } else if (px_i > pw || (px_i > 0 && px_f > pw)) { | |
347 | Scroll->scroll_to(MIN(x_i, x_f-pw), scr_y); | |
348 | } | |
349 | Scroll->redraw(); | |
350 | _MSG(" >>scr_x=%d btn0_x=%d\n", scr_x, Pack->child(0)->x()); | |
271 | 351 | } |
272 | 352 | |
273 | 353 | /* |
283 | 363 | if (cbtn->ui() != old_ui) { |
284 | 364 | // Set old tab label to normal color |
285 | 365 | if ((idx = get_btn_idx(old_ui)) != -1) { |
286 | btn = (CustTabButton*)child(idx); | |
366 | btn = (CustTabButton*)Pack->child(idx); | |
287 | 367 | btn->color(tabcolor_inactive); |
288 | 368 | btn->redraw(); |
289 | 369 | } |
290 | 370 | Wizard->value(cbtn->ui()); |
291 | 371 | cbtn->color(tabcolor_active); |
292 | 372 | cbtn->redraw(); |
373 | update_pack_offset(); | |
293 | 374 | |
294 | 375 | // Update window title |
295 | 376 | if ((bw = a_UIcmd_get_bw_by_widget(cbtn->ui()))) { |
304 | 385 | int idx; |
305 | 386 | |
306 | 387 | if ((idx = get_btn_idx((UI*)Wizard->value())) != -1) |
307 | switch_tab( (CustTabButton*)child(idx > 0 ? idx-1 : num_tabs()-1) ); | |
388 | switch_tab((CustTabButton*)Pack->child(idx>0 ? idx-1 : num_tabs()-1)); | |
308 | 389 | } |
309 | 390 | |
310 | 391 | void CustTabs::next_tab() |
312 | 393 | int idx; |
313 | 394 | |
314 | 395 | if ((idx = get_btn_idx((UI*)Wizard->value())) != -1) |
315 | switch_tab( (CustTabButton*)child((idx+1 < num_tabs()) ? idx+1 : 0) ); | |
396 | switch_tab((CustTabButton*)Pack->child((idx+1<num_tabs()) ? idx+1 : 0)); | |
316 | 397 | } |
317 | 398 | |
318 | 399 | /* |
325 | 406 | |
326 | 407 | if (idx != -1) { |
327 | 408 | // Make a label for this tab |
328 | size_t tab_chars = 7, label_len = strlen(label); | |
409 | size_t tab_chars = 15, label_len = strlen(label); | |
329 | 410 | |
330 | 411 | if (label_len > tab_chars) |
331 | 412 | tab_chars = a_Utf8_end_of_char(label, tab_chars - 1) + 1; |
334 | 415 | snprintf(title + tab_chars, 4, "..."); |
335 | 416 | |
336 | 417 | // Avoid unnecessary redraws |
337 | if (strcmp(child(idx)->label(), title)) { | |
338 | child(idx)->copy_label(title); | |
418 | if (strcmp(Pack->child(idx)->label(), title)) { | |
419 | int w = 0, h; | |
420 | Pack->child(idx)->copy_label(title); | |
421 | Pack->child(idx)->measure_label(w, h); | |
422 | Pack->child(idx)->size(w+14,ctab_h); | |
423 | update_pack_offset(); | |
339 | 424 | } |
340 | 425 | } |
341 | 426 | } |
344 | 429 | //---------------------------------------------------------------------------- |
345 | 430 | |
346 | 431 | static void win_cb (Fl_Widget *w, void *cb_data) { |
347 | int choice = 1; | |
348 | 432 | CustTabs *tabs = (CustTabs*) cb_data; |
349 | ||
350 | if (tabs->num_tabs() > 1) | |
433 | int choice = 1, ntabs = tabs->num_tabs(); | |
434 | ||
435 | if (ntabs > 1) | |
351 | 436 | choice = a_Dialog_choice5("Window contains more than one tab.", |
352 | 437 | "Close", "Cancel", NULL, NULL, NULL); |
353 | 438 | if (choice == 1) |
354 | while (tabs->num_tabs()) | |
439 | while (ntabs-- > 0) | |
355 | 440 | a_UIcmd_close_bw(a_UIcmd_get_bw_by_widget(tabs->wizard()->value())); |
356 | 441 | } |
357 | 442 | |
401 | 486 | |
402 | 487 | int focus = 1; |
403 | 488 | new_bw = UIcmd_tab_new(DilloTabs, old_ui, focus); |
404 | win->resizable(BW2UI(new_bw)); | |
489 | win->resizable(DilloTabs->wizard()); | |
405 | 490 | win->show(); |
406 | 491 | |
407 | 492 | if (old_bw == NULL && prefs.xpos >= 0 && prefs.ypos >= 0) { |
448 | 533 | // Copy the layout pointer into the bw data |
449 | 534 | new_bw->render_layout = (void*)layout; |
450 | 535 | |
536 | // Clear the window title | |
537 | if (focus) | |
538 | new_ui->window()->copy_label(new_ui->label()); | |
539 | ||
451 | 540 | // WORKAROUND: see findbar_toggle() |
452 | 541 | new_ui->findbar_toggle(0); |
453 | 542 | |
461 | 550 | { |
462 | 551 | BrowserWindow *bw = (BrowserWindow *)vbw; |
463 | 552 | UI *ui = BW2UI(bw); |
553 | CustTabs *tabs = ui->tabs(); | |
464 | 554 | Layout *layout = (Layout*)bw->render_layout; |
465 | 555 | |
466 | MSG("a_UIcmd_close_bw\n"); | |
556 | _MSG("a_UIcmd_close_bw\n"); | |
467 | 557 | a_Bw_stop_clients(bw, BW_Root + BW_Img + BW_Force); |
468 | 558 | delete(layout); |
469 | if (ui->tabs()) { | |
470 | ui->tabs()->remove_tab(ui); | |
559 | if (tabs) { | |
560 | tabs->remove_tab(ui); | |
561 | if (tabs->num_tabs() == 0) | |
562 | delete tabs->window(); | |
471 | 563 | } |
472 | 564 | a_Bw_free(bw); |
473 | 565 | } |
535 | 627 | { |
536 | 628 | if (url) { |
537 | 629 | a_Nav_push(bw, url, NULL); |
630 | BW2UI(bw)->focus_main(); | |
538 | 631 | } else { |
539 | 632 | // Used to start a bw with a blank screen |
540 | 633 | BW2UI(bw)->focus_location(); |
541 | 634 | a_UIcmd_set_buttons_sens(bw); |
542 | 635 | } |
543 | #if 0 | |
544 | if (BW2UI(bw)->get_panelmode() == UI_TEMPORARILY_SHOW_PANELS) | |
545 | BW2UI(bw)->set_panelmode(UI_HIDDEN); | |
546 | a_UIcmd_focus_main_area(bw); | |
547 | #endif | |
548 | 636 | } |
549 | 637 | |
550 | 638 | static void UIcmd_open_url_nbw(BrowserWindow *new_bw, const DilloUrl *url) |
785 | 873 | Dstr *ds = dStr_sized_new(128); |
786 | 874 | |
787 | 875 | /* parse search_url into label and url */ |
788 | a_Misc_parse_search_url(src, &l, &u); | |
789 | ||
790 | for (c = u; *c; c++) { | |
791 | if (*c == '%') | |
792 | switch(*++c) { | |
793 | case 's': | |
794 | dStr_append(ds, keys); break;; | |
795 | case '%': | |
796 | dStr_append_c(ds, '%'); break;; | |
797 | case 0: | |
798 | MSG_WARN("search_url ends with '%%'\n"); c--; break;; | |
799 | default: | |
800 | MSG_WARN("illegal specifier '%%%c' in search_url\n", *c); | |
801 | } | |
802 | else | |
803 | dStr_append_c(ds, *c); | |
876 | if (a_Misc_parse_search_url(src, &l, &u) == 0) { | |
877 | for (c = u; *c; c++) { | |
878 | if (*c == '%') | |
879 | switch(*++c) { | |
880 | case 's': | |
881 | dStr_append(ds, keys); break;; | |
882 | case '%': | |
883 | dStr_append_c(ds, '%'); break;; | |
884 | case 0: | |
885 | MSG_WARN("search_url ends with '%%'\n"); c--; break;; | |
886 | default: | |
887 | MSG_WARN("illegal specifier '%%%c' in search_url\n", *c); | |
888 | } | |
889 | else | |
890 | dStr_append_c(ds, *c); | |
891 | } | |
804 | 892 | } |
805 | 893 | dFree(keys); |
806 | 894 | |
846 | 934 | a_UIcmd_set_save_dir(prefs.save_dir); |
847 | 935 | |
848 | 936 | SuggestedName = UIcmd_make_save_filename(URL_STR(url)); |
849 | name = a_Dialog_save_file("Save Link as File", NULL, SuggestedName); | |
850 | MSG("a_UIcmd_save_link: %s\n", name); | |
937 | if ((name = a_Dialog_save_file("Save Link as File", NULL, SuggestedName))) { | |
938 | MSG("a_UIcmd_save_link: %s\n", name); | |
939 | a_Nav_save_url(bw, url, name); | |
940 | } | |
851 | 941 | dFree(SuggestedName); |
852 | ||
853 | if (name) { | |
854 | a_Nav_save_url(bw, url, name); | |
855 | } | |
856 | 942 | } |
857 | 943 | |
858 | 944 | /* |
1145 | 1231 | } |
1146 | 1232 | |
1147 | 1233 | /* |
1148 | * Set the page title in the window titlebar and tab label. | |
1234 | * Set the page title in the tab label and window titlebar. | |
1149 | 1235 | * (Update window titlebar for the current tab only) |
1150 | 1236 | */ |
1151 | 1237 | void a_UIcmd_set_page_title(BrowserWindow *bw, const char *label) |
1153 | 1239 | const int size = 128; |
1154 | 1240 | char title[size]; |
1155 | 1241 | |
1242 | if (snprintf(title, size, "Dillo: %s", label ? label : "") >= size) { | |
1243 | uint_t i = MIN(size - 4, 1 + a_Utf8_end_of_char(title, size - 8)); | |
1244 | snprintf(title + i, 4, "..."); | |
1245 | } | |
1246 | BW2UI(bw)->copy_label(title); | |
1247 | BW2UI(bw)->tabs()->set_tab_label(BW2UI(bw), label ? label : ""); | |
1248 | ||
1156 | 1249 | if (a_UIcmd_get_bw_by_widget(BW2UI(bw)->tabs()->wizard()->value()) == bw) { |
1157 | 1250 | // This is the focused bw, set window title |
1158 | if (snprintf(title, size, "Dillo: %s", label) >= size) { | |
1159 | uint_t i = MIN(size - 4, 1 + a_Utf8_end_of_char(title, size - 8)); | |
1160 | snprintf(title + i, 4, "..."); | |
1161 | } | |
1162 | BW2UI(bw)->copy_label(title); | |
1163 | 1251 | BW2UI(bw)->window()->copy_label(title); |
1164 | 1252 | } |
1165 | BW2UI(bw)->tabs()->set_tab_label(BW2UI(bw), label); | |
1166 | 1253 | } |
1167 | 1254 | |
1168 | 1255 | /* |
1196 | 1283 | BW2UI(bw)->button_set_sens(UI_BACK, sens); |
1197 | 1284 | // Forward |
1198 | 1285 | sens = (a_Nav_stack_ptr(bw) < a_Nav_stack_size(bw) - 1 && |
1199 | !bw->nav_expecting); | |
1286 | !a_Bw_expecting(bw)); | |
1200 | 1287 | BW2UI(bw)->button_set_sens(UI_FORW, sens); |
1201 | } | |
1202 | ||
1203 | /* | |
1204 | * Keep track of mouse pointer over a link. | |
1205 | */ | |
1206 | void a_UIcmd_set_pointer_on_link(BrowserWindow *bw, int flag) | |
1207 | { | |
1208 | BW2UI(bw)->pointerOnLink(flag); | |
1209 | } | |
1210 | ||
1211 | /* | |
1212 | * Is the mouse pointer over a link? | |
1213 | */ | |
1214 | int a_UIcmd_pointer_on_link(BrowserWindow *bw) | |
1215 | { | |
1216 | return BW2UI(bw)->pointerOnLink(); | |
1217 | 1288 | } |
1218 | 1289 | |
1219 | 1290 | /* |
77 | 77 | void a_UIcmd_set_page_title(BrowserWindow *bw, const char *label); |
78 | 78 | void a_UIcmd_set_msg(BrowserWindow *bw, const char *format, ...); |
79 | 79 | void a_UIcmd_set_buttons_sens(BrowserWindow *bw); |
80 | void a_UIcmd_set_pointer_on_link(BrowserWindow *bw, int flag); | |
81 | int a_UIcmd_pointer_on_link(BrowserWindow *bw); | |
82 | 80 | |
83 | 81 | #ifdef __cplusplus |
84 | 82 | } |
232 | 232 | dStr_append(SolvedUrl, BaseStr); |
233 | 233 | if ((p = strchr(SolvedUrl->str, '#'))) |
234 | 234 | dStr_truncate(SolvedUrl, p - SolvedUrl->str); |
235 | if (!BaseUrl->path) | |
236 | dStr_append_c(SolvedUrl, '/'); | |
235 | 237 | |
236 | 238 | if (RelUrl->query) { /* query */ |
237 | 239 | if (BaseUrl->query) |
46 | 46 | * (non '_'-ended macros MUST use these for initialization sake) |
47 | 47 | */ |
48 | 48 | /* these MAY return NULL: */ |
49 | #define URL_SCHEME_(u) u->scheme | |
50 | #define URL_AUTHORITY_(u) u->authority | |
51 | #define URL_PATH_(u) u->path | |
52 | #define URL_QUERY_(u) u->query | |
53 | #define URL_FRAGMENT_(u) u->fragment | |
49 | #define URL_SCHEME_(u) (u)->scheme | |
50 | #define URL_AUTHORITY_(u) (u)->authority | |
51 | #define URL_PATH_(u) (u)->path | |
52 | #define URL_QUERY_(u) (u)->query | |
53 | #define URL_FRAGMENT_(u) (u)->fragment | |
54 | 54 | #define URL_HOST_(u) a_Url_hostname(u) |
55 | #define URL_ALT_(u) u->alt | |
55 | #define URL_ALT_(u) (u)->alt | |
56 | 56 | #define URL_STR_(u) a_Url_str(u) |
57 | 57 | /* this returns a Dstr* */ |
58 | #define URL_DATA_(u) u->data | |
58 | #define URL_DATA_(u) (u)->data | |
59 | 59 | /* these return an integer */ |
60 | #define URL_PORT_(u) (URL_HOST(u), u->port) | |
61 | #define URL_FLAGS_(u) u->flags | |
62 | #define URL_ILLEGAL_CHARS_(u) url->illegal_chars | |
63 | #define URL_ILLEGAL_CHARS_SPC_(u) url->illegal_chars_spc | |
60 | #define URL_PORT_(u) (URL_HOST(u), (u)->port) | |
61 | #define URL_FLAGS_(u) (u)->flags | |
62 | #define URL_ILLEGAL_CHARS_(u) (u)->illegal_chars | |
63 | #define URL_ILLEGAL_CHARS_SPC_(u) (u)->illegal_chars_spc | |
64 | 64 | |
65 | 65 | /* |
66 | 66 | * Access methods that never return NULL. |
114 | 114 | styleAttrs.setBorderStyle (BORDER_SOLID); |
115 | 115 | styleAttrs.padding.setVal (1); |
116 | 116 | styleAttrs.backgroundColor = NULL; |
117 | styleAttrs.width = createPerLength (0.5); | |
118 | styleAttrs.height = createPerLength (0.5); | |
117 | styleAttrs.width = createPerLength (0.25); | |
118 | styleAttrs.height = createPerLength (0.25); | |
119 | 119 | |
120 | 120 | Style *imageStyle1 = Style::create (layout, &styleAttrs); |
121 | 121 | image1 = new dw::Image ("A longer ALT Text to demonstrate clipping."); |