text_view.cpp

Go to the documentation of this file.
00001 /*
00002  * File Name: text_view.cpp
00003  */
00004 
00005 /*
00006  * This file is part of uds-plugin-plaintext.
00007  *
00008  * uds-plugin-plaintext is free software: you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation, either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * uds-plugin-plaintext is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program. If not, see <http://www.gnu.org/licenses/>.
00020  */
00021 
00022 /**
00023  * Copyright (C) 2008 iRex Technologies B.V.
00024  * All rights reserved.
00025  */
00026 
00027 #include "log.h"
00028 #include "utils.h"
00029 #include "font_cache.h"
00030 #include "pango_renderer.h"
00031 
00032 #include "text_config.h"
00033 #include "text_view.h"
00034 #include "text_tasks.h"
00035 
00036 namespace text
00037 {
00038 
00039 TextView::TextView(TextModel *m)
00040 : model(m),
00041   ctrl(0),
00042   font_family(DEFAULT_ASCII_FONT),
00043   font_size(DEFAULT_FONT_SIZE),
00044   display_width(DEFAULT_SURFACE_WIDTH),
00045   display_height(DEFAULT_SURFACE_HEIGHT),
00046   dpi(DEFAULT_DPI),
00047   color_depth(DEFAULT_COLOR_DEPTH),
00048   line_height(0),
00049   lines_per_page(0),
00050   left_margin(DEFAULT_LEFT_MARGIN),
00051   right_margin(DEFAULT_RIGHT_MARGIN),
00052   top_margin(DEFAULT_TOP_MARGIN),
00053   bottom_margin(DEFAULT_BOTTOM_MARGIN),
00054   client_width(display_width - left_margin - right_margin),
00055   client_height(display_height - top_margin - bottom_margin),
00056   pango_context(0),
00057   pango_mutex(0),
00058   font_hash_code(0),
00059   is_repagination_needed(false),
00060   blank_lines(0)
00061 {
00062     // Special handling for Chinese font.
00063     const char *enc = model->get_encoding().c_str();
00064 #ifdef WIN32
00065     if (_stricmp(enc, "GB2312") == 0 || _stricmp(enc, "GBK") == 0)
00066 #else
00067     if (strcasecmp(enc, "GB2312") == 0 || strcasecmp(enc, "GBK") == 0)
00068 #endif
00069     {
00070         font_family = DEFAULT_CHINESE_FONT;
00071     }
00072     initialize();
00073 }
00074 
00075 TextView::~TextView()
00076 {
00077     deinitialize();
00078 }
00079 
00080 void TextView::initialize()
00081 {
00082     // Create mutex for synchronous access to pango stuff.
00083     pango_mutex = g_mutex_new();
00084 
00085     // Initialize pango context.
00086     pango_context_init();
00087 
00088     calculate_lines_per_page();
00089 }
00090 
00091 void TextView::deinitialize()
00092 {
00093     // Release pango stuff
00094     pango_context_final();
00095     g_mutex_free(pango_mutex);
00096 }
00097 
00098 void TextView::pango_context_init()
00099 {
00100     // Create PangoFT2FontMap
00101     PangoFT2FontMap *font_map = PANGO_FT2_FONT_MAP(pango_ft2_font_map_new());
00102 
00103     // Set dpi information.
00104     pango_ft2_font_map_set_resolution(font_map, dpi, dpi);
00105 
00106     // Create pango context
00107     pango_context = pango_ft2_font_map_create_context(font_map);
00108 
00109     // Create pango font description
00110     PangoFontDescription* font_desc = pango_font_description_new();
00111     pango_font_description_set_family(font_desc, font_family.c_str());
00112     pango_font_description_set_size(font_desc, font_size * PANGO_SCALE);
00113     pango_context_set_font_description(pango_context, font_desc);
00114 
00115     char *desc_str = pango_font_description_to_string(font_desc);
00116     font_hash_code = make_hash_code(desc_str, 
00117         static_cast<unsigned int>(strlen(desc_str)));
00118     g_free(desc_str);
00119 
00120     // Since pango_context has copied the font description from us,
00121     // our font description can be freed safely.
00122     pango_font_description_free(font_desc);
00123 }
00124 
00125 void TextView::pango_context_final()
00126 {
00127     // Release font caches in font map.
00128     PangoFT2FontMap* font_map = PANGO_FT2_FONT_MAP(pango_context_get_font_map(pango_context));
00129     pango_ft2_font_map_substitute_changed(font_map);
00130 
00131     // Release pango context, this will release font map and font description as well.
00132     if (pango_context != NULL)
00133     {
00134         g_object_unref(pango_context);
00135         pango_context = NULL;
00136     }
00137 }
00138 
00139 bool TextView::paginate(const Position& start_pos)
00140 {
00141 
00142     Position in_pos(0, 0);
00143     Position out_pos;
00144     bool forward = true;
00145     
00146     size_t page_count = pages.size();
00147     if (page_count == 0)
00148     {
00149         in_pos = start_pos;
00150     }
00151     else if (pages.back().end != Position(0, 0))
00152     {
00153         // The forward pagination is not finished yet.
00154         in_pos = pages.back().end;
00155     }
00156     else if (pages.front().start != Position(0, 0))
00157     {
00158         // The backward pagination is not finished yet.
00159         in_pos = pages.front().start;
00160         forward = false;
00161     }
00162     else
00163     {
00164         // The pagination is complete.
00165         return true;
00166     }
00167 
00168     if (forward)
00169     {
00170         calculate_next_page_pos(in_pos, out_pos);
00171         pages.push_back(PageInfo(in_pos, out_pos));
00172     }
00173     else
00174     {
00175         calculate_prev_page_pos(in_pos, out_pos);
00176         pages.push_front(PageInfo(out_pos, in_pos));
00177     }
00178     return false;
00179 }
00180 
00181 bool TextView::get_anchor_by_page(unsigned int page_index, Position& pos)
00182 {
00183     if (pages.size() == 0)
00184     {
00185         // Pagination hasn't been started yet.
00186         if (page_index == 0)
00187         {
00188             // We can render page 0 despite pagination is not ready.
00189             pos = Position(0, 0);
00190             return true;
00191         }
00192         else
00193         {
00194             return false;
00195         }
00196     }
00197     else
00198     {
00199         // Pagination starts, but maybe partial is successful.
00200         if (page_index >= pages.size())
00201         {
00202             return false;
00203         }
00204         else
00205         {
00206             pos = pages[page_index].start;
00207             return true;
00208         }
00209     }
00210 }
00211 
00212 PangoLayout* TextView::create_layout(const char *str, int len)
00213 {
00214     PangoLayout *layout = pango_layout_new(pango_context);
00215     assert(layout != NULL);
00216 
00217     pango_layout_set_width(layout, client_width * PANGO_SCALE);
00218     pango_layout_set_spacing(layout, DEFAULT_LINE_SPACING * PANGO_SCALE);
00219     pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
00220     pango_layout_set_justify(layout, TRUE);
00221     pango_layout_set_text(layout, str, len);
00222     pango_layout_set_single_paragraph_mode(layout, TRUE);
00223     return layout;
00224 }
00225 
00226 void TextView::calculate_lines_per_page()
00227 {
00228     // Calculate lines per page
00229     const std::string *str = model->get_paragraph(0);
00230     unsigned int sample_len = MIN(32, static_cast<int>(str->size()));
00231     PangoLayout *layout = create_layout(str->c_str(), sample_len);
00232 
00233     PangoRectangle  rect;
00234     PangoLayoutIter *iter = pango_layout_get_iter(layout);
00235     PangoLayoutLine *pango_line = pango_layout_iter_get_line_readonly(iter);
00236     pango_layout_line_get_pixel_extents(pango_line, NULL, &rect);
00237     line_height = rect.height;
00238     lines_per_page = (client_height + DEFAULT_LINE_SPACING) / (line_height + DEFAULT_LINE_SPACING);
00239     pango_layout_iter_free(iter);
00240 
00241     // Release PangoLayout object
00242     g_object_unref(layout);
00243 }
00244 
00245 bool TextView::calculate_prev_page_pos(const Position& in_pos, Position& out_pos)
00246 {
00247     // Check if we are already the first page.
00248     if (in_pos == Position(0, 0))
00249     {
00250         return false;
00251     }
00252 
00253     // Check if we can get next page anchor from page table.
00254     size_t page_count = pages.size();
00255     for (unsigned int i=0; i<page_count; i++)
00256     {
00257         if (pages[i].end == in_pos)
00258         {
00259             out_pos = pages[i].start;
00260             return true;
00261         }
00262     }
00263 
00264     g_mutex_lock(pango_mutex);
00265     unsigned int start_paragraph = in_pos.paragraph;
00266     unsigned int start_offset    = in_pos.offset;
00267     PangoLayout *layout = NULL;
00268     if (start_offset == 0)
00269     {
00270         start_paragraph--;
00271     }
00272 
00273     unsigned int total_lines = 0;
00274     while (total_lines < lines_per_page)
00275     {
00276         if (static_cast<int>(start_paragraph) < 0)
00277         {
00278             if (in_pos > Position(0, 0))
00279             {
00280                 blank_lines = lines_per_page - total_lines;
00281             }
00282             break;
00283         }
00284 
00285         if (layout != NULL)
00286         {
00287             g_object_unref(layout);
00288             layout = NULL;
00289         }
00290         const std::string *str = model->get_paragraph(start_paragraph);
00291         layout = create_layout(str->c_str(), static_cast<int>(str->size()));
00292 
00293         int line_count = pango_layout_get_line_count(layout);
00294         if (start_offset != 0)
00295         {
00296             // We are calculating from the second half part of a paragraph
00297             int line_offset, x_pos;
00298             pango_layout_index_to_line_x(layout, start_offset, FALSE, &line_offset, &x_pos);
00299             line_count = line_offset;
00300         }
00301 
00302         if (total_lines + line_count < lines_per_page)
00303         {
00304             // We have enough space to load more paragraphs for current page
00305             total_lines += line_count;
00306             start_paragraph--;
00307             start_offset = 0;
00308         }
00309         else if (total_lines + line_count == lines_per_page)
00310         {
00311             out_pos.paragraph = start_paragraph;
00312             out_pos.offset = 0;
00313             break;
00314         }
00315         else
00316         {
00317             // The remained space is not enough for current paragraph
00318             PangoLayoutIter *iter = pango_layout_get_iter(layout);
00319             unsigned int line_index = line_count - (lines_per_page - total_lines);
00320             for (unsigned int i=0; i<line_index; i++)
00321             {
00322                 pango_layout_iter_next_line(iter);
00323             }
00324 
00325             out_pos.paragraph = start_paragraph;
00326             out_pos.offset = pango_layout_iter_get_index(iter);
00327             pango_layout_iter_free(iter);
00328             break;
00329         }
00330     }
00331 
00332     if (layout != NULL)
00333     {
00334         g_object_unref(layout);
00335         layout = NULL;
00336     }
00337     g_mutex_unlock(pango_mutex);
00338     return (static_cast<int>(start_paragraph) >= 0);
00339 }
00340 
00341 bool TextView::calculate_next_page_pos(const Position& in_pos, Position& out_pos)
00342 {
00343     /// Check if we can get next page anchor from page table.
00344     size_t page_count = pages.size();
00345     for (unsigned int i=0; i<page_count; i++)
00346     {
00347         if (pages[i].start == in_pos)
00348         {
00349             out_pos = pages[i].end;
00350             return out_pos != Position(0, 0);
00351         }
00352     }
00353 
00354     unsigned int start_paragraph = in_pos.paragraph;
00355     unsigned int start_offset    = in_pos.offset;
00356     unsigned int total_lines = 0;
00357 
00358     g_mutex_lock(pango_mutex);
00359     PangoLayout *layout = NULL;
00360     while (total_lines < lines_per_page)
00361     {
00362         if (start_paragraph >= model->get_paragraph_count())
00363         {
00364             break;
00365         }
00366 
00367         if (layout != NULL)
00368         {
00369             g_object_unref(layout);
00370             layout = NULL;
00371         }
00372         const std::string *str = model->get_paragraph(start_paragraph);
00373         layout = create_layout(str->c_str(), static_cast<int>(str->size()));
00374 
00375         int line_count = pango_layout_get_line_count(layout);
00376         int line_offset = 0;
00377         if (start_offset != 0)
00378         {
00379             // We are calculating from the second half part of a paragraph
00380             int x_pos;
00381             pango_layout_index_to_line_x(layout, start_offset, FALSE, &line_offset, &x_pos);
00382             line_count -= line_offset;
00383         }
00384 
00385         if (total_lines + line_count < lines_per_page)
00386         {
00387             // We have enough space to load more paragraphs for current page
00388             total_lines += line_count;
00389             start_paragraph++;
00390             start_offset = 0;
00391         }
00392         else if (total_lines + line_count == lines_per_page)
00393         {
00394             if (++start_paragraph < model->get_paragraph_count())
00395             {
00396                 out_pos.paragraph = start_paragraph;
00397                 out_pos.offset = 0;
00398             }
00399             break;
00400         }
00401         else
00402         {
00403             // The remained space is not enough for current paragraph
00404             PangoLayoutIter *iter = pango_layout_get_iter(layout);
00405             for (unsigned int i=0; i<lines_per_page-total_lines+line_offset; i++)
00406             {
00407                 pango_layout_iter_next_line(iter);
00408             }
00409 
00410             out_pos.offset = pango_layout_iter_get_index(iter);
00411             out_pos.paragraph = start_paragraph;
00412             pango_layout_iter_free(iter);
00413             break;
00414         }
00415     }
00416 
00417     if (layout != NULL)
00418     {
00419         g_object_unref(layout);
00420         layout = NULL;
00421     }
00422     g_mutex_unlock(pango_mutex);
00423     return (start_paragraph < model->get_paragraph_count());
00424 }
00425 
00426 void TextView::check_page_table(const Position& rendering_pos)
00427 {
00428     size_t page_count = pages.size();
00429     for (unsigned int i=0; i<page_count; i++)
00430     {
00431         if (pages[i].start == rendering_pos)
00432         {
00433             return;
00434         }
00435     }
00436 
00437     // Can't find page boundaries for specified anchor.
00438     // Re-pagination is needed.
00439     is_repagination_needed = true;
00440 }
00441 
00442 unsigned int TextView::get_page_index_by_anchor(const Position& anchor)
00443 {
00444     /// Check if we can get next page anchor from page table.
00445     size_t page_count = pages.size();
00446 
00447     // Always return 0 if pagination is not complete.
00448     if (page_count == 0 || pages[page_count-1].end != Position(0, 0))
00449     {
00450         return 0;
00451     }
00452 
00453     for (unsigned int i=0; i<page_count; i++)
00454     {
00455         if (pages[i].end > anchor && pages[i].start <= anchor)
00456         {
00457             return i;
00458         }
00459     }
00460 
00461     return static_cast<unsigned int>(page_count-1);
00462 }
00463 
00464 Position TextView::get_page_anchor_by_anchor(const Position& anchor)
00465 {
00466     /// Check if we can get next page anchor from page table.
00467     size_t page_count = pages.size();
00468     for (unsigned int i=0; i<page_count; i++)
00469     {
00470         if (pages[i].end > anchor && pages[i].start <= anchor)
00471         {
00472             return pages[i].start;
00473         }
00474     }
00475 
00476     if (page_count>0 && pages[page_count-1].end == Position(0, 0))
00477     {
00478         return pages[page_count-1].start;
00479     }
00480 
00481     return Position(anchor.paragraph, 0);
00482 }
00483 
00484 unsigned int TextView::get_current_page_index()
00485 {
00486     return get_page_index_by_anchor(rendering_pos);
00487 }
00488 
00489 Position TextView::render(unsigned char *bmp, const Position& start_pos)
00490 {
00491     if (is_repagination_needed)
00492     {
00493         pages.clear();
00494         blank_lines = 0;
00495         ctrl->paginate(start_pos, true /* Send start notification */);
00496         is_repagination_needed = false;
00497     }
00498 
00499     g_mutex_lock(pango_mutex);
00500     unsigned int start_paragraph = start_pos.paragraph;
00501     unsigned int start_offset    = start_pos.offset;
00502     PangoLayout *layout = NULL;
00503 
00504     rendering_pos = start_pos;
00505 
00506     unsigned int total_lines = 0;
00507     if (start_pos == Position(0, 0))
00508     {
00509         total_lines = blank_lines;
00510     }
00511     int y_offset = (line_height + DEFAULT_LINE_SPACING)*total_lines*PANGO_SCALE;
00512 
00513     // If we don't start with a new paragraph, then we must render the
00514     // remained part of current paragraph first.
00515     if (start_offset != 0)
00516     {
00517         if (layout != NULL)
00518         {
00519             g_object_unref(layout);
00520             layout = NULL;
00521         }
00522         const std::string *str = model->get_paragraph(start_paragraph);
00523         layout = create_layout(str->c_str(), static_cast<int>(str->size()));
00524 
00525         PangoLayoutIter *iter = pango_layout_get_iter(layout);
00526 
00527         int line_offset, x_pos;
00528         pango_layout_index_to_line_x(layout, start_offset, FALSE, &line_offset, &x_pos);
00529         y_offset = (line_height + DEFAULT_LINE_SPACING)*line_offset*PANGO_SCALE;
00530 
00531         int baseline = 0;
00532         for (int i=0; i<line_offset; i++)
00533         {
00534             pango_layout_iter_next_line(iter);
00535         }
00536 
00537         do
00538         {
00539             baseline = pango_layout_iter_get_baseline(iter) - y_offset;
00540             PangoLayoutLine *pango_line = pango_layout_iter_get_line_readonly(iter);
00541             render_single_line(bmp,
00542                                pango_line,
00543                                font_hash_code,
00544                                left_margin * PANGO_SCALE,
00545                                baseline+top_margin*PANGO_SCALE,
00546                                (display_width+3)/4*4);
00547             if (++total_lines == lines_per_page)
00548             {
00549                 // Calculate the offset of the rightmost character.
00550                 int index, trailing;
00551                 pango_layout_line_x_to_index(pango_line, client_width - 1, &index, &trailing);
00552                 start_offset = index;
00553                 pango_layout_iter_free(iter);
00554                 goto RenderEnd;
00555             }
00556         } while (pango_layout_iter_next_line(iter));
00557         
00558         // Release iter since we finish with it.
00559         pango_layout_iter_free(iter);
00560 
00561         start_paragraph++;
00562         y_offset = (line_height + DEFAULT_LINE_SPACING)*total_lines*PANGO_SCALE;
00563     }
00564 
00565     // Render the remained paragraphs
00566     while (start_paragraph < model->get_paragraph_count())
00567     {
00568         // Render current paragraph
00569         if (layout != NULL)
00570         {
00571             g_object_unref(layout);
00572             layout = NULL;
00573         }
00574         const std::string *str = model->get_paragraph(start_paragraph);
00575         layout = create_layout(str->c_str(), static_cast<int>(str->size()));
00576 
00577         PangoLayoutIter *iter = pango_layout_get_iter(layout);
00578         int baseline = 0;
00579         do 
00580         {
00581             baseline = pango_layout_iter_get_baseline(iter) + y_offset;
00582             PangoLayoutLine *pango_line = pango_layout_iter_get_line_readonly(iter);
00583             render_single_line(bmp, 
00584                                pango_line,
00585                                font_hash_code,
00586                                left_margin * PANGO_SCALE,
00587                                baseline+top_margin*PANGO_SCALE,
00588                                (display_width+3)/4*4);
00589             if (++total_lines == lines_per_page)
00590             {
00591                 int index, trailing;
00592                 pango_layout_line_x_to_index(pango_line, client_width - 1, &index, &trailing);
00593                 start_offset = index;
00594                 pango_layout_iter_free(iter);
00595                 goto RenderEnd;
00596             }
00597         } while (pango_layout_iter_next_line(iter));
00598         pango_layout_iter_free(iter);
00599 
00600         start_paragraph++;
00601         y_offset = (line_height + DEFAULT_LINE_SPACING)*total_lines*PANGO_SCALE;
00602     }
00603 
00604 RenderEnd:
00605     // Release pango layout generated by render.
00606     if (layout != NULL)
00607     {
00608         g_object_unref(layout);
00609         layout = NULL;
00610     }
00611     g_mutex_unlock(pango_mutex);
00612 
00613     return Position(start_paragraph, start_offset);
00614 }
00615 
00616 bool TextView::set_font_size(int font_size)
00617 {
00618     if (this->font_size == font_size)
00619     {
00620         return true;
00621     }
00622 
00623     this->font_size = font_size;
00624 
00625     g_mutex_lock(pango_mutex);
00626 
00627     // Release font caches in font map.
00628     PangoFT2FontMap* font_map = PANGO_FT2_FONT_MAP(pango_context_get_font_map(pango_context));
00629     pango_ft2_font_map_substitute_changed(font_map);
00630 
00631     // Create new pango font description and copy it to pango context.
00632     PangoFontDescription* font_desc = pango_font_description_new();
00633     pango_font_description_set_family(font_desc, font_family.c_str());
00634     pango_font_description_set_size(font_desc, font_size * PANGO_SCALE);
00635     pango_context_set_font_description(pango_context, font_desc);
00636 
00637     char *desc_str = pango_font_description_to_string(font_desc);
00638     font_hash_code = make_hash_code(desc_str, 
00639         static_cast<unsigned int>(strlen(desc_str)));
00640     g_free(desc_str);
00641 
00642     // Since pango_context has copied the font description from us,
00643     // our font description can be freed safely.
00644     pango_font_description_free(font_desc);
00645     calculate_lines_per_page();
00646 
00647     // Change the repagination flag to true.
00648     is_repagination_needed = true;
00649 
00650     // Clear font cache.
00651     FontCache& font_cache = FontCache::instance();
00652     font_cache.clear();
00653 
00654     g_mutex_unlock(pango_mutex);
00655     return true;
00656 }
00657 
00658 void TextView::set_display_size(unsigned int width, unsigned int height)
00659 {
00660     LOGPRINTF("\n\nwidth=%d, height=%d\n\n", width, height);
00661     if (width == display_width && height == display_height)
00662     {
00663         return;
00664     }
00665 
00666    g_mutex_lock(pango_mutex);
00667     if (height != display_height)
00668     {
00669         // Display height changed. The current layout can be reused.
00670         // But we need to re-calculate lines per page.
00671         display_height = height;
00672         client_height = display_height - top_margin - bottom_margin;
00673         calculate_lines_per_page();
00674     }
00675 
00676     if (width != display_width)
00677     {
00678         display_width = width;
00679         client_width = display_width - left_margin - right_margin;
00680     }
00681 
00682     // Change the repagination flag to true.
00683     is_repagination_needed = true;
00684     g_mutex_unlock(pango_mutex);
00685 }
00686 
00687 bool TextView::map_doc_pos_to_view_pos(const Position& page_anchor,
00688                                        const Position& doc_pos,
00689                                        ViewPosition&   view_pos,
00690                                        bool            trailing)
00691 {
00692     if (doc_pos < page_anchor)
00693     {
00694         return false;
00695     }
00696 
00697     unsigned int line_height_with_spacing = line_height + DEFAULT_LINE_SPACING;
00698 
00699     // Calculate the line offset and x offset inside layout corresponding to doc pos.
00700     const std::string *str = model->get_paragraph(doc_pos.paragraph);
00701     PangoLayout *layout = create_layout(str->c_str(), static_cast<int>(str->size()));
00702 
00703     int line_offset_pos, x_offset_pos;
00704     pango_layout_index_to_line_x(layout, doc_pos.offset, trailing, &line_offset_pos, &x_offset_pos);
00705 
00706     unsigned int total_lines = 0;
00707     unsigned int start_paragraph = page_anchor.paragraph;
00708     unsigned int start_offset    = page_anchor.offset;
00709     if (start_paragraph == 0 && start_offset == 0)
00710     {
00711         total_lines += blank_lines;
00712     }
00713 
00714     if (start_offset != 0)
00715     {
00716         // The page does not start with a new paragraph.
00717         if (layout != NULL)
00718         {
00719             g_object_unref(layout);
00720             layout = NULL;
00721         }
00722         const std::string *str = model->get_paragraph(start_paragraph);
00723         layout = create_layout(str->c_str(), static_cast<int>(str->size()));
00724 
00725         int line_offset_start, x_pos;
00726         pango_layout_index_to_line_x(layout, start_offset, FALSE, &line_offset_start, &x_pos);
00727 
00728         if (doc_pos.paragraph == start_paragraph)
00729         {
00730             total_lines = line_offset_pos - line_offset_start;
00731             goto MapEnd;
00732         }
00733         else
00734         {
00735             total_lines = pango_layout_get_line_count(layout) - line_offset_start;
00736             start_paragraph++;
00737         }
00738     }
00739 
00740     for (; start_paragraph != doc_pos.paragraph; start_paragraph++)
00741     {
00742         if (layout != NULL)
00743         {
00744             g_object_unref(layout);
00745             layout = NULL;
00746         }
00747         const std::string *str = model->get_paragraph(start_paragraph);
00748         layout = create_layout(str->c_str(), static_cast<int>(str->size()));
00749 
00750         total_lines += pango_layout_get_line_count(layout);
00751     }
00752 
00753     total_lines += line_offset_pos;
00754 
00755 MapEnd:
00756     if (layout != NULL)
00757     {
00758         g_object_unref(layout);
00759         layout = NULL;
00760     }
00761     if (total_lines < lines_per_page)
00762     {
00763         view_pos.x = PANGO_PIXELS(x_offset_pos) + left_margin;
00764         view_pos.y = top_margin + line_height_with_spacing * total_lines;
00765         return true;
00766     }
00767     else
00768     {
00769         return false;
00770     }
00771 }
00772 
00773 bool TextView::map_view_pos_to_doc_pos(const Position&     page_anchor,
00774                                        const ViewPosition& view_pos,
00775                                        Position&           doc_pos)
00776 {
00777     int x_offset_pos = view_pos.x - left_margin;
00778     int y_offset_pos = view_pos.y - top_margin;
00779 
00780     unsigned int start_paragraph = page_anchor.paragraph;
00781     unsigned int start_offset    = page_anchor.offset;
00782     unsigned int line_height_with_spacing = line_height + DEFAULT_LINE_SPACING;
00783 
00784     if (start_paragraph == 0 && start_offset == 0)
00785     {
00786         y_offset_pos -= blank_lines * line_height_with_spacing;
00787     }
00788 
00789     PangoLayout *layout = NULL;
00790     bool found = false;
00791 
00792     if (start_offset != 0)
00793     {
00794         const std::string *str = model->get_paragraph(start_paragraph);
00795         layout = create_layout(str->c_str(), static_cast<int>(str->size()));
00796 
00797         int line_offset_start, x_pos;
00798         pango_layout_index_to_line_x(layout, start_offset, FALSE, &line_offset_start, &x_pos);
00799 
00800         int lines_left = pango_layout_get_line_count(layout) - line_offset_start;
00801         if (static_cast<int>(lines_left * line_height_with_spacing) > y_offset_pos)
00802         {
00803             // The corresponding doc pos is inside this paragraph.
00804             doc_pos.paragraph = start_paragraph;
00805 
00806             // Calculate offset.
00807             int bytes_index, trailing;
00808             pango_layout_xy_to_index(layout, 
00809                                      x_offset_pos * PANGO_SCALE,
00810                                      (line_offset_start*line_height_with_spacing+y_offset_pos) * PANGO_SCALE,
00811                                      &bytes_index, &trailing);
00812             doc_pos.offset = bytes_index;
00813             found = true;
00814             goto MapEnd;
00815         }
00816         else
00817         {
00818             y_offset_pos -= lines_left * line_height_with_spacing;
00819             start_paragraph++;
00820         }
00821     }
00822 
00823     for (; start_paragraph < model->get_paragraph_count(); start_paragraph++)
00824     {
00825         if (layout != NULL)
00826         {
00827             g_object_unref(layout);
00828             layout = NULL;
00829         }
00830         const std::string *str = model->get_paragraph(start_paragraph);
00831         layout = create_layout(str->c_str(), static_cast<int>(str->size()));
00832 
00833         int line_count = pango_layout_get_line_count(layout);
00834         if (static_cast<int>(line_count * line_height_with_spacing) > y_offset_pos)
00835         {
00836             break;
00837         }
00838         else
00839         {
00840             y_offset_pos -= line_count * line_height_with_spacing;
00841         }
00842     }
00843 
00844     if (start_paragraph == model->get_paragraph_count())
00845     {
00846         goto MapEnd;
00847     }
00848 
00849     int bytes_index, trailing;
00850     pango_layout_xy_to_index(layout, x_offset_pos * PANGO_SCALE, y_offset_pos * PANGO_SCALE, &bytes_index, &trailing);
00851     doc_pos.paragraph = start_paragraph;
00852     doc_pos.offset    = bytes_index;
00853     found = true;
00854 
00855 MapEnd:
00856     if (layout)
00857     {
00858         g_object_unref(layout);
00859     }
00860     return found;
00861 }
00862 
00863 bool TextView::get_bounding_rectangles(const Position& page_anchor,
00864                                        std::vector<Rect>& bounding_rect,
00865                                        const Range& range)
00866 {
00867     // A flag indicating that the start position and end position of specified
00868     // range is in different pages.
00869     bool span_pages = false;
00870     ViewPosition view_pos_start, view_pos_end;
00871       
00872     if (!map_doc_pos_to_view_pos(page_anchor, range.start, view_pos_start, false))
00873     {
00874         // The range's start position is not inside the page which starts with
00875         // page_anchor.
00876         return false;
00877     }
00878 
00879     if (!map_doc_pos_to_view_pos(page_anchor, range.end, view_pos_end, true))
00880     {
00881         // The range's end position is not inside the page which starts with
00882         // page_anchor.
00883         span_pages = true;
00884     }
00885 
00886     unsigned int line_height_with_spacing = line_height + DEFAULT_LINE_SPACING;
00887 
00888     // Check if thhe start position and end position are in the same line.
00889     if (view_pos_start.y == view_pos_end.y)
00890     {
00891         // The start position and end position are in the same line.
00892         bounding_rect.push_back(Rect(view_pos_start.x,
00893                                      view_pos_start.y + DEFAULT_LINE_SPACING,
00894                                      view_pos_end.x - view_pos_start.x,
00895                                      line_height));
00896     }
00897     else
00898     {
00899         // More lines.
00900         bounding_rect.push_back(Rect(view_pos_start.x,
00901                                      view_pos_start.y + DEFAULT_LINE_SPACING,
00902                                      display_width - view_pos_start.x - right_margin,
00903                                      line_height));
00904 
00905         for (unsigned int y_offset = view_pos_start.y+line_height_with_spacing;
00906              static_cast<int>(y_offset) < view_pos_end.y &&
00907              y_offset < top_margin + line_height_with_spacing * lines_per_page;
00908              y_offset += line_height_with_spacing)
00909         {
00910             bounding_rect.push_back(Rect(left_margin,
00911                                          y_offset + DEFAULT_LINE_SPACING,
00912                                          client_width,
00913                                          line_height));
00914         }
00915 
00916         if (!span_pages)
00917         {
00918             bounding_rect.push_back(Rect(left_margin,
00919                                          view_pos_end.y + DEFAULT_LINE_SPACING,
00920                                          view_pos_end.x - left_margin,
00921                                          line_height));
00922         }
00923     }
00924 
00925     return true;
00926 }
00927 
00928 }
Generated by  doxygen 1.6.2-20100208