~stomato463/+junk/nvdajp

« back to all changes in this revision

Viewing changes to nvdaHelper/remote/displayModel.cpp

  • Committer: Masataka Shinke
  • Date: 2011-10-25 12:35:26 UTC
  • mfrom: (4185 jpmain)
  • mto: This revision was merged to the branch mainline in revision 4211.
  • Revision ID: mshinke@users.sourceforge.jp-20111025123526-ze527a2rl3z0g2ky
lp:~nishimotz/nvdajp/main : 4185 をマージ

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
This file is a part of the NVDA project.
 
3
URL: http://www.nvda-project.org/
 
4
Copyright 2006-2010 NVDA contributers.
 
5
    This program is free software: you can redistribute it and/or modify
 
6
    it under the terms of the GNU General Public License version 2.0, as published by
 
7
    the Free Software Foundation.
 
8
    This program is distributed in the hope that it will be useful,
 
9
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
11
This license can be found at:
 
12
http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 
13
*/
 
14
 
 
15
#include <string>
 
16
#include <sstream>
 
17
#include <deque>
 
18
#include <list>
 
19
#include <set>
 
20
#include <common/xml.h>
 
21
#include "nvdaControllerInternal.h"
 
22
#include "log.h"
 
23
#include "displayModel.h"
 
24
 
 
25
using namespace std;
 
26
 
 
27
void displayModelChunk_t::generateXML(wstring& text) {
 
28
        wstringstream s;
 
29
        s<<L"<text";
 
30
        s<<L" font-name=\""<<formatInfo.fontName<<L"\" ";
 
31
        s<<L" font-size=\""<<formatInfo.fontSize<<L"pt\" ";
 
32
        if(this->formatInfo.bold) s<<L" bold=\"true\"";
 
33
        if(this->formatInfo.italic) s<<L" italic=\"true\"";
 
34
        if(this->formatInfo.underline) s<<L" underline=\"true\"";
 
35
        s<<L" color=\""<<this->formatInfo.color<<L"\"";
 
36
        s<<L" background-color=\""<<this->formatInfo.backgroundColor<<L"\"";
 
37
        s<<L">";
 
38
        text.append(s.str());
 
39
        for(wstring::iterator i=this->text.begin();i!=this->text.end();++i) {
 
40
                appendCharToXML(*i,text);
 
41
        }
 
42
        text.append(L"</text>");
 
43
}
 
44
 
 
45
void displayModelChunk_t::truncate(int truncatePointX, BOOL truncateBefore) {
 
46
        if(text.length()==0) return;
 
47
        nhAssert(characterXArray.size()!=0);
 
48
        deque<int>::iterator c=characterXArray.begin();
 
49
        wstring::iterator t=text.begin();
 
50
        if(truncateBefore&&rect.left<truncatePointX) {
 
51
                for(;t!=text.end()&&(*c)<truncatePointX;++c,++t);
 
52
                if(c!=characterXArray.end()) rect.left=*c; else rect.left=rect.right; 
 
53
                characterXArray.erase(characterXArray.begin(),c);
 
54
                text.erase(text.begin(),t);
 
55
        } else if(!truncateBefore&&truncatePointX<rect.right) {
 
56
                for(;t!=text.end()&&(*c)<=truncatePointX;++c,++t);
 
57
                if(t!=text.begin()) {
 
58
                        --c;
 
59
                        --t;
 
60
                }
 
61
                rect.right=*c;
 
62
                characterXArray.erase(c,characterXArray.end());
 
63
                text.erase(t,text.end());
 
64
        }
 
65
}
 
66
 
 
67
displayModel_t::displayModel_t(): LockableAutoFreeObject(), chunksByYX() {
 
68
        LOG_DEBUG(L"created instance at "<<this);
 
69
}
 
70
 
 
71
displayModel_t::~displayModel_t() {
 
72
        LOG_DEBUG(L"destroying instance at "<<this);
 
73
        for(displayModelChunksByPointMap_t::iterator i=chunksByYX.begin();i!=chunksByYX.end();) {
 
74
                LOG_DEBUG(L"deleting chunk at "<<i->second);
 
75
                delete i->second;
 
76
                chunksByYX.erase(i++);
 
77
        }
 
78
}
 
79
 
 
80
size_t displayModel_t::getChunkCount() {
 
81
        return chunksByYX.size();
 
82
}
 
83
 
 
84
void displayModel_t::insertChunk(const RECT& rect, int baselineFromTop, const wstring& text, int* characterEndXArray, const displayModelFormatInfo_t& formatInfo, const RECT* clippingRect) {
 
85
        displayModelChunk_t* chunk=new displayModelChunk_t;
 
86
        LOG_DEBUG(L"created new chunk at "<<chunk);
 
87
        chunk->rect=rect;
 
88
        chunk->baselineFromTop=baselineFromTop;
 
89
        chunk->text=text;
 
90
        chunk->formatInfo=formatInfo;
 
91
        chunk->characterXArray.push_back(rect.left);
 
92
        for(unsigned int i=0;i<(text.length()-1);++i) chunk->characterXArray.push_back(characterEndXArray[i]+rect.left); 
 
93
        LOG_DEBUG(L"filled in chunk with rectangle from "<<rect.left<<L","<<rect.top<<L" to "<<rect.right<<L","<<rect.bottom<<L" with text of "<<text);
 
94
        //If a clipping rect is specified, and the chunk falls outside the clipping rect
 
95
        //Truncate the chunk so that it stays inside the clipping rect.
 
96
        if(clippingRect) {
 
97
                if(clippingRect->left>chunk->rect.left) chunk->truncate(clippingRect->left,TRUE);
 
98
                if(clippingRect->right<chunk->rect.right) chunk->truncate(clippingRect->right,FALSE);
 
99
        }
 
100
        //Its possible there is now no text in the chunk
 
101
        //Only insert it if there is text.
 
102
        if(chunk->text.length()>0) {
 
103
                insertChunk(chunk);
 
104
        } else {
 
105
                delete chunk;
 
106
        }
 
107
}
 
108
 
 
109
void displayModel_t::insertChunk(displayModelChunk_t* chunk) {
 
110
        chunksByYX[make_pair(chunk->rect.top+chunk->baselineFromTop,chunk->rect.left)]=chunk;
 
111
}
 
112
 
 
113
void displayModel_t::clearAll() {
 
114
        for(displayModelChunksByPointMap_t::iterator i=chunksByYX.begin();i!=chunksByYX.end();) {
 
115
                delete i->second;
 
116
                chunksByYX.erase(i++);
 
117
        }
 
118
}
 
119
 
 
120
void displayModel_t::clearRectangle(const RECT& rect, BOOL clearForText) {
 
121
        LOG_DEBUG(L"Clearing rectangle from "<<rect.left<<L","<<rect.top<<L" to "<<rect.right<<L","<<rect.bottom);
 
122
        set<displayModelChunk_t*> chunksForInsertion;
 
123
        displayModelChunksByPointMap_t::iterator i=chunksByYX.begin();
 
124
        RECT tempRect;
 
125
        while(i!=chunksByYX.end()) {
 
126
                displayModelChunksByPointMap_t::iterator nextI=i;
 
127
                ++nextI; 
 
128
                displayModelChunk_t* chunk=i->second;
 
129
                int baseline=i->first.first;
 
130
                if(IntersectRect(&tempRect,&rect,&(chunk->rect))) {
 
131
                        //The clearing rectangle intercects the chunk's rectangle in some way.
 
132
                        if(tempRect.bottom<=baseline) {
 
133
                                //The clearing rectangle some how covers the chunk below its baseline.
 
134
                                //If we're clearing to make room for text, or the clearing rectangle starts from the very top of the chunk
 
135
                                //Then we should shrink the chunk down so it stays below the clearing rectangle.
 
136
                                //If not, then we pretend the clearRectangle did not happen (the chunk was only parcially cleared vertically so we don't care).
 
137
                                if(clearForText||tempRect.top==chunk->rect.top) {
 
138
                                        chunk->rect.top=tempRect.bottom;
 
139
                                }
 
140
                        } else if(tempRect.top>baseline) {
 
141
                                //The clearing rectangle some how covers the chunk above its baseline.
 
142
                                //If we're clearing to make room for text, or the clearing rectangle starts from the very bottom of the chunk
 
143
                                //Then we should shrink the chunk up so it stays above the clearing rectangle.
 
144
                                //If not, then we pretend the clearRectangle did not happen (the chunk was only parcially cleared vertically so we don't care).
 
145
                                if(clearForText||tempRect.bottom==chunk->rect.bottom) {
 
146
                                        chunk->rect.bottom=tempRect.top;
 
147
                                }
 
148
                        } else {
 
149
                                //The clearing rectangle covers the chunk's baseline, so remove the part of the chunk covered horozontally by the clearing rectangle.
 
150
                                if(tempRect.left==chunk->rect.left&&tempRect.right==chunk->rect.right) {
 
151
                                        chunksByYX.erase(i);
 
152
                                        delete chunk;
 
153
                                } else if(tempRect.left>chunk->rect.left&&tempRect.right==chunk->rect.right) {
 
154
                                        chunk->truncate(tempRect.left,FALSE);
 
155
                                        if(chunk->text.length()==0) {
 
156
                                                chunksByYX.erase(i);
 
157
                                                delete chunk;
 
158
                                        }
 
159
                                } else if(tempRect.right<chunk->rect.right&&tempRect.left==chunk->rect.left) {
 
160
                                        chunksByYX.erase(i);
 
161
                                        chunk->truncate(tempRect.right,TRUE);
 
162
                                        if(chunk->text.length()==0) {
 
163
                                                delete chunk;
 
164
                                        } else {
 
165
                                                chunksForInsertion.insert(chunk);
 
166
                                        }
 
167
                                } else {
 
168
                                        displayModelChunk_t* newChunk=new displayModelChunk_t(*chunk);
 
169
                                        chunk->truncate(tempRect.left,FALSE);
 
170
                                        if(chunk->text.length()==0) {
 
171
                                                chunksByYX.erase(i);
 
172
                                                delete chunk;
 
173
                                        }
 
174
                                        newChunk->truncate(tempRect.right,TRUE);
 
175
                                        if(newChunk->text.length()==0) {
 
176
                                                delete newChunk;
 
177
                                        } else {
 
178
                                                chunksForInsertion.insert(newChunk);
 
179
                                        }
 
180
                                }
 
181
                        }
 
182
                }
 
183
                i=nextI;
 
184
        }
 
185
        for(set<displayModelChunk_t*>::iterator i=chunksForInsertion.begin();i!=chunksForInsertion.end();++i) {
 
186
                insertChunk(*i);
 
187
        }
 
188
        LOG_DEBUG(L"complete");
 
189
}
 
190
 
 
191
void displayModel_t::copyRectangle(const RECT& srcRect, BOOL removeFromSource, BOOL opaqueCopy, int destX, int destY, const RECT* destClippingRect, displayModel_t* destModel) {
 
192
        if(!destModel) destModel=this;
 
193
        RECT tempRect;
 
194
        int deltaX=destX-srcRect.left;
 
195
        int deltaY=destY-srcRect.top;
 
196
        RECT destRect={srcRect.left+deltaX,srcRect.top+deltaY,srcRect.right+deltaX,srcRect.bottom+deltaY};
 
197
        //If a clipping rectangle is provided, clip the destination rectangle
 
198
        if(destClippingRect) {
 
199
                IntersectRect(&tempRect,&destRect,destClippingRect);
 
200
                destRect=tempRect;
 
201
        }
 
202
        //Make copies of all the needed chunks, tweek their rectangle coordinates, truncate if needed, and store them in a temporary list
 
203
        list<displayModelChunk_t*> copiedChunks;
 
204
        for(displayModelChunksByPointMap_t::iterator i=chunksByYX.begin();i!=chunksByYX.end();++i) {
 
205
                //We only care about chunks that are overlapped by the source rectangle 
 
206
                if(!IntersectRect(&tempRect,&srcRect,&(i->second->rect))) continue; 
 
207
                //Copy the chunk
 
208
                displayModelChunk_t* chunk=new displayModelChunk_t(*(i->second));
 
209
                //Tweek its rectangle coordinates to match where its going in the destination model
 
210
                (chunk->rect.left)+=deltaX;
 
211
                (chunk->rect.top)+=deltaY;
 
212
                (chunk->rect.right)+=deltaX;
 
213
                (chunk->rect.bottom)+=deltaY;
 
214
                //Tweek its character x coordinates to match where its going in the destination model
 
215
                for(deque<int>::iterator x=chunk->characterXArray.begin();x!=chunk->characterXArray.end();++x) (*x)+=deltaX;
 
216
                //Truncate the chunk so it does not stick outside of the destination rectangle
 
217
                if(chunk->rect.left<destRect.left) {
 
218
                        chunk->truncate(destRect.left,TRUE);
 
219
                }
 
220
                if(chunk->rect.right>destRect.right) {
 
221
                        chunk->truncate(destRect.right,FALSE);
 
222
                }
 
223
                //if the chunk is now empty due to truncation then just delete it and move on to the next 
 
224
                if(chunk->text.length()==0) {
 
225
                        delete chunk;
 
226
                        continue;
 
227
                }
 
228
                //Insert the chunk in to the temporary list
 
229
                copiedChunks.insert(copiedChunks.end(),chunk);
 
230
        }
 
231
        //Clear the source rectangle if requested to do so (move rather than copy)
 
232
        if(removeFromSource) this->clearRectangle(srcRect);
 
233
        //Clear the entire destination rectangle if requested to do so
 
234
        if(opaqueCopy) destModel->clearRectangle(destRect);
 
235
        //insert the copied chunks in to the destination model
 
236
        for(list<displayModelChunk_t*>::iterator i=copiedChunks.begin();i!=copiedChunks.end();++i) {
 
237
                if(!opaqueCopy) destModel->clearRectangle((*i)->rect);
 
238
                destModel->insertChunk(*i);
 
239
        }
 
240
}
 
241
 
 
242
void displayModel_t::renderText(const RECT& rect, const int minHorizontalWhitespace, const int minVerticalWhitespace, const bool stripOuterWhitespace, bool useXML, wstring& text, deque<RECT>& characterRects) {
 
243
        RECT tempRect;
 
244
        wstring curLineText;
 
245
        deque<RECT> curLineCharacterRects;
 
246
        int curLineMinTop=-1;
 
247
        int curLineMaxBottom=-1;
 
248
        int curLineBaseline=-1;
 
249
        int lastChunkRight=rect.left;
 
250
        int lastLineBottom=rect.top;
 
251
        //Walk through all the chunks looking for any that intersect the rectangle
 
252
        displayModelChunksByPointMap_t::iterator chunkIt=chunksByYX.begin();
 
253
        while(chunkIt!=chunksByYX.end()) {
 
254
                displayModelChunk_t* chunk=NULL;
 
255
                BOOL isTempChunk=FALSE;
 
256
                if(IntersectRect(&tempRect,&rect,&(chunkIt->second->rect))) {
 
257
                        chunk=chunkIt->second;
 
258
                        //If this chunk is not fully covered by the rectangle
 
259
                        //Copy it and truncate it so that it is fully covered
 
260
                        if(chunk->rect.left<tempRect.left||chunk->rect.right>tempRect.right) {
 
261
                                chunk=new displayModelChunk_t(*chunk);
 
262
                                isTempChunk=TRUE;
 
263
                                if(chunk->rect.left<tempRect.left) chunk->truncate(tempRect.left,TRUE);
 
264
                                if(chunk->rect.right>tempRect.right) chunk->truncate(tempRect.right,FALSE);
 
265
                        }
 
266
                }
 
267
                //Find out the current line's baseline
 
268
                curLineBaseline=chunkIt->first.first;
 
269
                //Iterate to the next possible chunk
 
270
                ++chunkIt;
 
271
                //If we have a valid chunk then add it to the current line
 
272
                if(chunk&&chunk->text.length()>0) {
 
273
                        //Update the maximum height of the line
 
274
                        if(curLineText.length()==0) {
 
275
                                curLineMinTop=chunk->rect.top;
 
276
                                curLineMaxBottom=chunk->rect.bottom;
 
277
                        } else {
 
278
                                if(chunk->rect.top<curLineMinTop) curLineMinTop=chunk->rect.top;
 
279
                                if(chunk->rect.bottom>curLineMaxBottom) curLineMaxBottom=chunk->rect.bottom;
 
280
                        }
 
281
                        //Add space before this chunk if necessary
 
282
                        if(((chunk->rect.left-lastChunkRight)>=minHorizontalWhitespace)&&(lastChunkRight>rect.left||!stripOuterWhitespace)) {
 
283
                                if(useXML) {
 
284
                                        curLineText+=L"<text> </text>";
 
285
                                } else {
 
286
                                        curLineText+=L'\0';
 
287
                                }
 
288
                                tempRect.left=lastChunkRight;
 
289
                                tempRect.top=curLineBaseline-1;
 
290
                                tempRect.right=chunk->rect.left;
 
291
                                tempRect.bottom=curLineBaseline+1;
 
292
                                curLineCharacterRects.push_back(tempRect);
 
293
                        }
 
294
                        //Add text from this chunk to the current line
 
295
                        if(useXML) {
 
296
                                chunk->generateXML(curLineText);
 
297
                        } else {
 
298
                                curLineText.append(chunk->text);
 
299
                        }
 
300
                        //Copy the character X positions from this chunk  in to the current line
 
301
                        deque<int>::const_iterator cxaIt=chunk->characterXArray.begin();
 
302
                        while(cxaIt!=chunk->characterXArray.end()) {
 
303
                                tempRect.left=*cxaIt;
 
304
                                tempRect.top=chunk->rect.top;
 
305
                                ++cxaIt;
 
306
                                tempRect.right=(cxaIt!=chunk->characterXArray.end())?*cxaIt:chunk->rect.right;
 
307
                                tempRect.bottom=chunk->rect.bottom;
 
308
                                curLineCharacterRects.push_back(tempRect);
 
309
                        }
 
310
                lastChunkRight=chunk->rect.right;
 
311
                }
 
312
                if((chunkIt==chunksByYX.end()||chunkIt->first.first>curLineBaseline)&&curLineText.length()>0) {
 
313
                        //This is the end of the line
 
314
                        if(((curLineMinTop-lastLineBottom)>=minVerticalWhitespace)&&(lastLineBottom>rect.top||!stripOuterWhitespace)) {
 
315
                                //There is space between this line and the last,
 
316
                                //Insert a blank line in between.
 
317
                                text+=(useXML?L"<text>\n</text>":L"\n");
 
318
                                tempRect.left=rect.left;
 
319
                                tempRect.top=lastLineBottom;
 
320
                                tempRect.right=rect.right;
 
321
                                tempRect.bottom=curLineMinTop;
 
322
                                characterRects.push_back(tempRect);
 
323
                        }
 
324
                        //Insert this line in to the output.
 
325
                        text.append(curLineText);
 
326
                        characterRects.insert(characterRects.end(),curLineCharacterRects.begin(),curLineCharacterRects.end());
 
327
                        //Add a linefeed to complete the line
 
328
                        text+=(useXML?L"<text>\n</text>":L"\n");
 
329
                        tempRect.left=lastChunkRight;
 
330
                        tempRect.top=curLineBaseline-1;
 
331
                        tempRect.right=rect.right;
 
332
                        tempRect.bottom=curLineBaseline+1;
 
333
                        characterRects.push_back(tempRect);
 
334
                        //Reset the current line values
 
335
                        curLineText.clear();
 
336
                        curLineCharacterRects.clear();
 
337
                        lastChunkRight=rect.left;
 
338
                        lastLineBottom=curLineMaxBottom;
 
339
                        if(chunk&&isTempChunk) delete chunk;
 
340
                }
 
341
        }
 
342
        if(!stripOuterWhitespace&&(rect.bottom-lastLineBottom)>=minVerticalWhitespace) {
 
343
                //There is a gap between the bottom of the final line and the bottom of the requested rectangle,
 
344
                //So add a blank line.
 
345
                text+=(useXML?L"<text>\n</text>":L"\n");
 
346
                tempRect.left=rect.left;
 
347
                tempRect.top=lastLineBottom;
 
348
                tempRect.right=rect.right;
 
349
                tempRect.bottom=rect.bottom;
 
350
                characterRects.push_back(tempRect);
 
351
        }
 
352
}