ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/gclib/gfview/FXGView.cpp
Revision: 45
Committed: Tue Sep 6 18:19:20 2011 UTC (13 years, 1 month ago) by gpertea
File size: 58292 byte(s)
Log Message:
added gfview files

Line File contents
1 #include "fxkeys.h"
2 #include "fx.h"
3 #include <math.h>
4 #include "FXGView.h"
5 #include "clrutils.h"
6 #include "stdlib.h"
7 /*******************************************************************************/
8
9 // Map
10 FXDEFMAP(FXGView) FXGViewMap[]={
11 FXMAPFUNC(SEL_PAINT,0,FXGView::onPaint),
12 FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXGView::onLeftBtnPress),
13 FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXGView::onLeftBtnRelease),
14 FXMAPFUNC(SEL_CLICKED,0,FXGView::onClicked),
15 FXMAPFUNC(SEL_MOTION,0,FXGView::onMotion),
16 FXMAPFUNC(SEL_COMMAND,0,FXGView::onCommand),
17 FXMAPFUNC(SEL_QUERY_TIP,0,FXGView::onQueryTip),
18 FXMAPFUNC(SEL_QUERY_HELP,0,FXGView::onQueryHelp),
19 FXMAPFUNC(SEL_TIMEOUT,FXList::ID_TIPTIMER,FXGView::onTipTimer)
20 };
21
22 // Object implementation
23 FXIMPLEMENT(FXGView,FXScrollArea,FXGViewMap,ARRAYNUMBER(FXGViewMap))
24
25 /*******************************************************************************/
26 #define CL_BORDER 6 //border size (around the whole cluster layout)
27
28 ClSeq::ClSeq(FXGView* v, LytSeqInfo* lytseq, const char* seq) {
29 name=lytseq->name;
30 len=lytseq->length(); //except when segmented
31 sequence=Gstrdup(seq);
32 view=v;
33 flag=0;
34 group = -1;
35 cdsofs = 0;
36 clipL=lytseq->left-1;
37 clipR=lytseq->length()-lytseq->right;
38 cl_xpos=lytseq->offs;
39 cl_ypos=-1;
40 ins=0;
41 dels=0;
42 numsegs=0;
43 segs=NULL;
44 isET= (name.find("np|")>=0 || name.find("et|")>=0 ||
45 name.find("egad|")>=0)?1:0;
46 reversed=(lytseq->reversed==1);
47 //--- now, add the segment definitions, if any:
48 if (lytseq->numisegs>0) {
49 numsegs=lytseq->numisegs+1;
50 GMALLOC(segs,numsegs*sizeof(ClSeqSegment));
51 int segStart=lytseq->offs;
52 char LSpl=0;
53 int segSeqOfs=0;
54 int segLClip=clipL; //now these "segment" coords DO include clipped ends
55 //while those coords stored in LytSeqInfo::segments EXCLUDE clipping
56 int remainingLen=len; //sequence "length" left (excluding introns!)
57 int nonclipStart=lytseq->offs+clipL;
58 for (int i=0;i<lytseq->numisegs;i++) {
59 segs[i].xL = segStart;
60 segs[i].xR = lytseq->intersegs[i].segEnd+lytseq->intersegs[i].segRClip;
61 segs[i].clipL = segLClip;
62 segs[i].clipR = lytseq->intersegs[i].segRClip;
63 segs[i].splL=LSpl;
64 segs[i].splR=lytseq->intersegs[i].segRSplice;
65 segs[i].seqofs=segSeqOfs;
66 segSeqOfs=lytseq->intersegs[i].nextSegSeq;
67 segStart=lytseq->intersegs[i].nextStart-lytseq->intersegs[i].nextLClip;
68 //+1; --not needed because interseg coords are in fact
69 // given as seg-end coordinates!
70 segLClip=lytseq->intersegs[i].nextLClip;
71 LSpl=lytseq->intersegs[i].nextLSplice;
72 remainingLen-=(segs[i].xR-segs[i].xL+1);
73 nonclipStart=lytseq->intersegs[i].nextStart;
74 }//for each intersegment interval
75 //--add the final segment:
76 segs[numsegs-1].xL = segStart;
77 segs[numsegs-1].clipL = segLClip;
78 segs[numsegs-1].splL=LSpl;
79 if (v->filetype=='L') {
80 segs[numsegs-1].xR = segStart+remainingLen-2;
81 len=segStart+remainingLen-lytseq->offs-1;
82 }
83 else {
84 segs[numsegs-1].xR = lytseq->offs+len-1;
85 }
86 segs[numsegs-1].clipR = clipR;
87 segs[numsegs-1].splR=0;
88 segs[numsegs-1].seqofs=segSeqOfs;
89 }//segmented sequence
90 else { //not segmented: segment = whole sequence
91 addSegment(lytseq->offs, lytseq->offs+lytseq->length()-1, clipL, clipR);
92 }
93 }//FXClSeq constructor from LytSeqInfo
94
95 // Serialization
96 FXGView::FXGView():rows(false,true,false), columns(false,true,false) {
97 flags|=FLAG_ENABLED;
98 font=(FXFont*)-1;
99 seqfont=(FXFont*)-1;
100 gridH=12;
101 showContig=false;
102 seqfntH=12;
103 seqfntW=8;
104 seqH=seqfntH;
105 seqW=seqfntW;
106 grpColors[0]=NULL;
107 grpColors[1]=NULL;
108 XRight=0;
109 XLeft=0;
110 selSeq=NULL;
111 content_h=0;
112 content_w=0;
113 selCol=-1;
114 scale.sx=1;
115 scale.sy=1;
116 vSpace=seqH/4;
117 backbuf=NULL;
118 seqlist=NULL;
119 filetype=0;
120 //seqaligns=NULL;
121 colorStyle=csDefault;
122 canShowSeq=false;
123 hasSeqs=false;
124 maxThickness=0;
125 contig=NULL;
126 }
127
128 // Public constructor
129 FXGView::FXGView(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h):
130 FXScrollArea(p,opts,x,y,w,h), rows(false,true,false), columns(false,true,false){
131
132 seqlist=new GList<ClSeq>(false,true,false);
133 //seqaligns=new GList<ClAlign>(false,true,false);
134 flags|=FLAG_ENABLED;
135 font=new FXFont(getApp(), "helvetica", 8, FXFont::Normal);
136 seqfont=new FXFont(getApp(),"courier", 10, FXFont::Normal);
137 backbuf=NULL;
138 gridH=12;
139 grpColors[0]=NULL;
140 grpColors[1]=NULL;
141 showContig=false;
142 seqfntH=12; //seqfont->getFontHeight();
143 seqfntW=8; //seqfont->getFontWidth();
144 seqH=seqfntH;
145 seqW=seqfntW;
146 XRight=0;
147 XLeft=0;
148 selSeq=NULL;
149 selCol=-1;
150 vSpace=seqH/4;
151 scale.sx=1;
152 scale.sy=1;
153 target=tgt;
154 message=sel;
155 filetype=0;
156 colorStyle=csDefault;
157 //colorStyle=csDensity;
158 canShowSeq=false;
159 hasSeqs=false;
160 contig=NULL;
161 maxThickness=0;
162 //initSeqColors();
163 }
164
165 // Enter window
166 long FXGView::onEnter(FXObject* sender,FXSelector sel,void* ptr){
167 FXScrollArea::onEnter(sender,sel,ptr);
168 getApp()->addTimeout(this,ID_TIPTIMER,getApp()->getMenuPause());
169 lastMousePos.x=-1;
170 return 1;
171 }
172 // Leave window
173 long FXGView::onLeave(FXObject* sender,FXSelector sel,void* ptr){
174 FXScrollArea::onLeave(sender,sel,ptr);
175 getApp()->removeTimeout(this,ID_TIPTIMER);
176 lastMousePos.x=-1;
177 return 1;
178 }
179
180 // We timed out, i.e. the user didn't move for a while
181 long FXGView::onTipTimer(FXObject*,FXSelector,void*){
182 FXTRACE((250,"%s::onTipTimer %p\n",getClassName(),this));
183 flags|=FLAG_TIP;
184 return 1;
185 }
186
187 // We were asked about tip text
188 long FXGView::onQueryTip(FXObject* sender,FXSelector,void*){
189 if((flags&FLAG_TIP)){ // No tip when autoselect!
190 int col;
191 ClSeq* seq=getSeqAt(lastMousePos.x, lastMousePos.y, col); //get item under cursor - this might take a while!
192 if(seq!=NULL){
193 FXString string=seq->name;
194 sender->handle(this,MKUINT(ID_SETSTRINGVALUE,SEL_COMMAND),(void*)&string);
195 return 1;
196 }
197 }
198 return 0;
199 }
200
201 // We were asked about status text
202 long FXGView::onQueryHelp(FXObject* sender,FXSelector,void*){
203 if((flags&FLAG_HELP)) {
204 FXTRACE((250,"%s::onQueryHelp %p\n",getClassName(),this));
205 int col;
206 ClSeq* seq=getSeqAt(lastMousePos.x, lastMousePos.y, col);
207 //also compute the approximate layout position
208 int xchpos = col+XLeft;
209 FXString help;
210 help.format("[ offset: %d ] ",xchpos);
211 if (seq!=NULL) {
212 help+=seq->name;
213 if (!seq->comment.empty())
214 help+=" | "+seq->comment;
215 }
216 sender->handle(this,MKUINT(ID_SETSTRINGVALUE,SEL_COMMAND),(void*)&help);
217 return 1;
218 }
219 return 0;
220 }
221
222
223 // Command message
224 long FXGView::onCommand(FXObject*,FXSelector,void* ptr){
225 return target && target->handle(this,MKUINT(message,SEL_COMMAND),ptr);
226 }
227
228 // Mouse moved
229 long FXGView::onMotion(FXObject*,FXSelector,void* ptr){
230 FXEvent* event=(FXEvent*)ptr;
231 FXuint flg=flags;
232 // Kill the tip
233 flags&=~FLAG_TIP;
234 // Kill the tip timer
235 getApp()->removeTimeout(this,ID_TIPTIMER);
236 lastMousePos.x=event->win_x;
237 lastMousePos.y=event->win_y;
238 // Reset tip timer if nothing's going on
239 getApp()->addTimeout(this,ID_TIPTIMER,getApp()->getMenuPause());
240 if (target)
241 target->handle(this,MKUINT(message,SEL_MOTION),ptr);
242 // Get item we're over
243 //cursor=getItemAt(event->win_x,event->win_y);
244 // Force GUI update only when needed
245 //return (cursor!=oldcursor)||(flg&FLAG_TIP);
246 return (flg&FLAG_TIP);
247 }
248
249
250 void FXGView::drawColumn(int colno) {
251 if (colno<0) return;
252 update(iround(seqW*colno)+CL_BORDER+getXPosition(), 0, iround(seqW)+1, viewport_h);
253 }
254
255 // Pressed a button
256 long FXGView::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
257 FXEvent* event=(FXEvent*)ptr;
258 flags&=~FLAG_TIP;
259 handle(this,MKUINT(0,SEL_FOCUS_SELF),ptr);
260 if (isEnabled()) {
261 grab();
262 flags&=~FLAG_UPDATE;
263 // Locate seq under cursor
264 btnSeq=getSeqAt(event->win_x,event->win_y, btnCol);
265 if (btnCol>=0) {
266 if (selCol==btnCol) { //deselect selCol
267 int c=selCol;
268 selCol=-1;
269 drawColumn(c);
270 }
271 else {
272 //select btnCol, deselect selCol
273 int c=selCol; //previous selection
274 selCol=btnCol; //new selection
275 if (c>=0) drawColumn(c);
276 drawColumn(selCol);
277 }
278 }//column selection
279 // First change callback
280 if (target && target->handle(this,MKUINT(message,SEL_LEFTBUTTONPRESS),ptr))
281 return 1;
282 // No seq hit
283 flags|=FLAG_PRESSED;
284 // Change current selection - not yet
285 //selSeq=btnSeq;
286 return 1;
287 }
288 return 0;
289 }
290
291 //select a sequence and optionally bring it into view
292 void FXGView::selectSeq(ClSeq* seq, bool showIt) {
293 if (seq==NULL) {
294 //deselect sequence
295 ClSeq* p=selSeq;
296 selSeq=NULL;
297 if (p!=NULL) drawSeq(p);
298 }
299 else {//real selection
300 //select btnSeq, deselect selSeq
301 ClSeq* p=selSeq; //previous selection
302 selSeq=seq;
303 if (p!=NULL) drawSeq(p);
304 drawSeq(selSeq);
305 if (showIt)
306 makeVisible(selSeq);
307 }
308 }
309
310 void FXGView::makeVisible(ClSeq* seq) {
311 CRect rect;
312 seq->calcViewRect(rect); //the position of the seq rectangle in the view space
313 //we'll try to determine how to scroll to get this sequence shown in the middle of the screen
314 int yscroll = getYPosition();
315 int xscroll = getXPosition();
316 int cx=width/2 - xscroll; //view's x position of the center of the screen
317 int cy=height/2 - yscroll; //view's y position of the center
318 scrollBy(rect.ix()-cx,rect.iy()-cy);
319 }
320
321 // Released button
322 long FXGView::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
323 FXEvent* event=(FXEvent*)ptr;
324 if (!isEnabled()) return 0;
325 ungrab();
326 flags|=FLAG_UPDATE;
327 flags&=~FLAG_PRESSED;
328 // First chance callback
329 if(target && target->handle(this,MKUINT(message,SEL_LEFTBUTTONRELEASE),ptr)) return 1;
330 // No activity
331 //if(!(flg&FLAG_PRESSED) && !(options&LIST_AUTOSELECT)) return 1;
332
333 // Scroll to make item visibke
334 //makeItemVisible(current);
335
336 // Generate clicked callbacks, but only if a Seq was clicked:
337 int col;
338 ClSeq* seq=getSeqAt(event->win_x, event->win_y, col);
339 if (btnSeq==NULL)
340 selectSeq(NULL); //deselect
341 else
342 if (btnSeq==seq)
343 selectSeq(btnSeq);
344 if(event->click_count==1){
345 handle(this,MKUINT(0,SEL_CLICKED),(void*)btnSeq);
346 }
347 else if(event->click_count==2){
348 handle(this,MKUINT(0,SEL_DOUBLECLICKED),(void*)btnSeq);
349 }
350 else if(event->click_count==3){
351 handle(this,MKUINT(0,SEL_TRIPLECLICKED),(void*)btnSeq);
352 }
353 handle(this,MKUINT(0,SEL_COMMAND),(void*)btnSeq);
354
355 return 1;
356 }
357
358 // Clicked in list
359 long FXGView::onClicked(FXObject*,FXSelector,void* ptr){
360 return target && target->handle(this,MKUINT(message,SEL_CLICKED),ptr);
361 }
362
363
364 /*
365 void FXGView::initGrpColors(int num) {
366 GFREE(grpColors);
367 GMALLOC(grpColors, sizeof(FXColor)*num);
368 FXColor c=FXRGB(0x80, 0,0);
369
370 int step=255/(num+1);
371 if (step==0) step=10;
372 for (int i=0;i<num;i++) {
373 c= modhls(c, step, 0, 0);
374 if (i % 2)
375 grpColors[num-i-1]=c;
376 else
377 grpColors[i]=c;
378 }
379 } */
380
381 bool FXGView::setSeqGrp(FXString& seq, int grp) {
382 //seqs must be sorted by name
383 ClSeq test(this, seq.text(), 200);
384 int sid=-1;
385 if ((sid=seqlist->IndexOf(&test))<0)
386 return false;
387 seqlist->Get(sid)->group=grp;
388 return true;
389 }
390
391 void FXGView::initColors(FXColor* seqcolors, FXColor* basecolors,
392 FXColor* matchcolors, FXColor* ctgquals, FXColor* grpcolors, FXColor* grpgapcolors) {
393 gridColor=getApp()->getBaseColor();
394 gridColorH=makeHiliteColor(makeHiliteColor(makeHiliteColor(gridColor)));
395 gridColorSh=makeShadowColor(makeShadowColor(makeShadowColor(gridColor)));
396 seqColors=seqcolors;
397 baseColors=basecolors;
398 matchColors=matchcolors;
399 ctgQuals=ctgquals;
400 grpColors[0]=grpcolors;
401 grpColors[1]=grpgapcolors;
402 /*
403 //matchColors[16]=makeHiliteColor(makeHiliteColor(fxcolorfromname("Red")));
404 matchColors[16]=mismatch;
405 ctgQuals[16]=getApp()->getBaseColor();
406 ctgQuals[15]=0xFF00FFFF;
407 matchColors[15]=fxcolorfromname("darkBlue");
408 for (int i=0;i<15;i++) {
409 matchColors[14-i]=modhls(matchColors[15], 0, 10*i, -5*i);
410 ctgQuals[14-i]=modhls(ctgQuals[15], 0, -4*i, 0);
411 }
412
413 buffer for color array to pass to
414 the Seq Paint method
415 0 = seq color
416 1 = seq highlight
417 2 = seq shadow
418 3 = seqtext color
419 4 = seqtrim color
420 5 = seq range color
421 6 = seq select color
422 7 = alternate seq color (ET)
423 8 = inter-segment connector color
424 9 = internal trim color
425 10 = major consensus splice site
426 11 = minor consensus splice site
427 */
428 /*
429 //-- base sequence color:
430 seqColors[0]=fxcolorfromname("darkBlue");
431 // Hex Colors are BGR style !
432 //-- highlight border:
433 seqColors[1]=makeHiliteColor(fxcolorfromname("Gray50"));
434 //--shadow border:
435 seqColors[2]=makeShadowColor(fxcolorfromname("Gray20"));
436 //seqColors[2]=0x0020A040;
437 seqColors[3]=getApp()->getSelforeColor();
438 //-- clipping color:
439 //seqColors[4]=fxcolorfromname("Gray50"); //trimmed ends
440 seqColors[4]=fxcolorfromname("MistyRose3"); //trimmed ends
441 seqColors[5]=fxcolorfromname("Green");
442 seqColors[6]=fxcolorfromname("Yellow");
443 //-- NP color:
444 seqColors[7]=fxcolorfromname("darkRed");
445 //seqColors[7]=0x0000A000; //greenish
446 //-- inter-segment gap line:
447 //seqColors[8]=fxcolorfromname("Red");
448 //seqColors[8]=0x006060F0;
449 seqColors[8]=fxcolorfromname("LightSkyBlue3");
450 //-- internal clipping color:
451 //seqColors[9]=fxcolorfromname("DarkSalmon");
452 seqColors[9]=fxcolorfromname("MistyRose1");
453 //-- splice site consensus
454 seqColors[10]=fxcolorfromname("Red"); //hard
455 seqColors[11]=fxcolorfromname("DarkPink"); //soft
456
457 baseColors[0]=fxcolorfromname("White"); //font color
458 baseColors[1]=fxcolorfromname("DarkGreen"); //A
459 baseColors[2]=fxcolorfromname("Blue"); //C
460 baseColors[3]=fxcolorfromname("Orange"); //G
461 baseColors[4]=fxcolorfromname("Red"); //T
462 baseColors[5]=fxcolorfromname("Gray20"); // N,*, other codes
463 */
464 }
465
466 // Cleanup
467 FXGView::~FXGView(){
468 getApp()->removeTimeout(this,ID_TIPTIMER);
469 delete contig;
470 delete font;
471 font=(FXFont*)-1;
472 //GFREE(grpColors);
473 delete seqfont;
474 delete backbuf;
475 delete seqlist;
476 //delete seqaligns;
477 }
478
479
480 // Create window
481 void FXGView::create(){
482 FXScrollArea::create();
483 font->create();
484 seqfont->create();
485 seqfntH=seqfont->getFontHeight()+2;
486 seqfntW=seqfont->getFontWidth();
487 if (backbuf==NULL) {
488 backbuf=new FXImage(getApp(),
489 NULL, IMAGE_SHMI|IMAGE_SHMP, width,height);
490 }
491 backbuf->create(); //needed?
492 RecalcContent();
493 }
494
495
496 // Detach window
497 void FXGView::detach(){
498 FXScrollArea::detach();
499 font->detach();
500 seqfont->detach();
501 backbuf->detach();
502 }
503
504
505 // Move content
506 void FXGView::scrollBy(int dx,int dy){
507 vertical->setPosition(-pos_y+dy);
508 pos_y=-vertical->getPosition();
509 horizontal->setPosition(-pos_x+dx);
510 pos_x=-horizontal->getPosition();
511 update();
512 }
513
514
515
516 void FXGView::resize(FXint w,FXint h) {
517 if(w!=width || h!=height){
518 backbuf->resize(w,h);
519 }
520 FXScrollArea::resize(w,h);
521 }
522
523 void FXGView::position(FXint x, FXint y, FXint w,FXint h) {
524 if(w!=width || h!=height){
525 if (backbuf!=NULL)
526 backbuf->resize(w,h);
527 }
528 FXScrollArea::position(x,y,w,h);
529 }
530
531
532 inline FXbool IntersectRect(const CRect& r1, const CRect& r2) {
533 return
534 !((r1.x + r1.w <= r2.x) || (r2.x + r2.w <= r1.x)
535 || (r1.y + r1.h <= r2.y) || (r2.y + r2.h <= r1.y));
536 }
537
538
539 inline FXbool FXIntersectRect(const FXRectangle& r1, const FXRectangle& r2) {
540 return
541 !((r1.x + r1.w <= r2.x) || (r2.x + r2.w <= r1.x)
542 || (r1.y + r1.h <= r2.y) || (r2.y + r2.h <= r1.y));
543 }
544
545 //computes the intersection of two rectangles
546 CRect RIntersection(CRect& r1, CRect& r2) {
547 CRect R; //this is the result;
548 if (IntersectRect(r1,r2)) {
549 R.x = FXMAX (r1.x, r2.x);
550 R.y = FXMAX (r1.y, r2.y);
551 R.w = FXMIN(r1.x+r1.w, r2.x+r2.w) - R.x;
552 R.h = FXMIN(r1.y+r1.h, r2.y+r2.h) - R.y;
553 }
554 return R;
555 }
556
557 //computes the intersection of two rectangles
558 FXRectangle FXRIntersection(FXRectangle& r1, FXRectangle& r2) {
559 FXRectangle R(0,0,0,0); //this is the result;
560 if (FXIntersectRect(r1,r2)) {
561 R.x = FXMAX (r1.x, r2.x);
562 R.y = FXMAX (r1.y, r2.y);
563 R.w = FXMIN(r1.x+r1.w, r2.x+r2.w) - R.x;
564 R.h = FXMIN(r1.y+r1.h, r2.y+r2.h) - R.y;
565 }
566 return R;
567 }
568
569 inline void copyFXRect(CRect& rect, const FXRectangle& src) {
570 rect.x=src.x;rect.y=src.y;
571 rect.w=src.w;rect.h=src.h;
572 }
573
574 inline void offsetRect(CRect& rect, int offsx, int offsy) {
575 rect.x+=offsx;
576 rect.y+=offsy;
577 }
578
579 inline void offsetFXRect(FXRectangle& rect, int offsx, int offsy) {
580 rect.x+=offsx;
581 rect.y+=offsy;
582 }
583
584 //int ClSeq::xTran(int x) {
585 //takes x as a sequence coordinate and computes the current view
586 //}
587
588
589 void ClSeq::calcViewRect(CRect& seqrect) {
590 seqrect.x= view->seqW*(double)(cl_xpos - view->XLeft)+(double)CL_BORDER;
591 seqrect.y=(view->seqH + view->vSpace)*cl_ypos + view->gridH+CL_BORDER;
592 seqrect.w=view->seqW*(double)len+2; //?? is this 2 pixel correction really needed?
593 seqrect.h=view->seqH;
594 }
595
596 void ClSeq::calcSegView(int segidx, CRect& segrect) {
597 //compute a seq segment's position
598 //within the view
599 segrect.x= view->seqW*(double)(segs[segidx].xL - view->XLeft)+(double)CL_BORDER;
600 segrect.y=(view->seqH + view->vSpace)*cl_ypos + view->gridH+CL_BORDER;
601 segrect.w=view->seqW*(double)(segs[segidx].xR-segs[segidx].xL+1)+2; //?? is this 2 pixel correction really needed?
602 segrect.h=view->seqH;
603 }
604
605 /*FXbool ClSeq::InRect(const CRect& rect, CRect& seqrect) {
606 //for testing if repainting is needed for this object
607 //rect is in pixels, but already offset to the whole cluster area
608 //also computes the real-world coordinates into seqrect
609 //seqH and seqW must already be computed (acording to scale!)
610 calcViewRect(seqrect);
611 //to account for rounding errors, enlarge the rectangle a bit:
612 //seqrect.x--;seqrect.y--;
613 //seqrect.w++;seqrect.h++;
614 //return IntersectRect(seqrect,rect);
615
616 return (view->canShowSeq)?(seqrect.x+seqrect.w+view->seqfntW >rect.x
617 && seqrect.x-view->seqfntW<rect.x+rect.w):
618 (seqrect.x+seqrect.w>rect.x && seqrect.x<rect.x+rect.w);
619 } */
620
621 static int nt2color(char c) {
622 switch(c) {
623 case 'a':
624 case 'A':return 1;
625 case 'c':
626 case 'C':return 2;
627 case 'g':
628 case 'G':return 3;
629 case 't':
630 case 'T':return 4;
631 }
632 return 5;
633 }
634
635
636 ClSeq* FXGView::getSeqAt(int x, int y, int& col) {
637 //locate the sequence that's on screen at coords x,y
638 //locate the row:
639 x-=getXPosition();
640 y-=getYPosition();
641 col=-1;
642 int r0=(y-gridH-CL_BORDER)/(seqH+vSpace); //layout row
643 int colno=(int)floor(((double)(x-CL_BORDER))/seqW);
644 if (colno>=0 && colno<columns.Count()) col=colno;
645 if (r0<0 || r0>=rows.Count())
646 return NULL;
647
648 if ((y-gridH-CL_BORDER) % (seqH+vSpace) > seqH)
649 return NULL; //hit between rows
650
651 int xchpos = colno + XLeft; //layout column
652 //for each of the sequences on this row (they are sorted by cl_xpos)
653 for (int i=0;i<rows[r0]->seqs.Count();i++) {
654 ClSeq* s=rows[r0]->seqs[i];
655 if (xchpos>=s->cl_xpos && xchpos<=s->cl_xpos+s->len-1)
656 return s;
657 }
658 return NULL;
659 }
660
661
662 void FXGView::drawTriangle(FXDCWindow& dc, FXColor color, FXint l,FXint t,FXint r,FXint b){
663 FXPoint points[3];
664 int m=(t+b)/2;
665 dc.setForeground(color);
666 points[0].x=l;
667 points[0].y=t;
668 points[1].x=l;
669 points[1].y=b;
670 points[2].x=r;
671 points[2].y=m;
672 dc.fillPolygon(points,3);
673 }
674
675
676 void FXGView::drawSeq(ClSeq* seq) {
677 //redraw a particular sequence
678 CRect rect;
679 seq->calcViewRect(rect);
680 offsetRect(rect, getXPosition(), getYPosition());
681 update(rect.ix()-1,rect.iy()-1,rect.iw()+1,rect.ih()+1);
682 /* directly on the surface dc ?
683 seq->Paint(dc, rect);
684 */
685 }
686
687 void FXGView::paintSeq(FXDCWindow* dc, FXRectangle& ClpRct,
688 ClSeq* seq, ColorStyle colorstyle) {
689 //ClpRct is the rectangle that needs to be repainted
690 //
691 int xp = -getXPosition();
692 int yp = -getYPosition();
693 int margin=CL_BORDER<<1;
694 int endx=ClpRct.x + ClpRct.w+1; //end X coordinate for the "dirty" rectangle
695 //--first (leftmost) column of the layout which needs painting:
696 int pLcol=(int)floor(((double)(ClpRct.x+xp-CL_BORDER))/seqW);
697 //--last (rightmost) column of the layout which needs painting:
698 int pRcol=(int)floor(((double)(endx+xp-CL_BORDER))/seqW);
699 int dXL=(int)ClpRct.x-margin; //left px coord of dirty rectangle
700 int dXR=(int)ClpRct.x+(int)ClpRct.w+margin; // right px coord of dirty
701 int lastXend=INT_MIN; //for inter-segment connector drawing only
702 int gapLx=0;
703 int gapRx=0;
704 char gapspL=0;//is there a splice site on the left side of the gap?
705 char gapspR=0;//is there a splice site on the right side of the gap?
706 FXColor gapcolor=seq->group>=0 ? grpColors[1][seq->group] : seqColors[8];
707 //bool showSeq=(canShowSeq && !seq->sequence.empty());
708 bool showSeq=canShowSeq && seq->sequence!=NULL;
709 for (int iseg=0;iseg<seq->numsegs;iseg++) {
710 CRect segrect;
711 seq->calcSegView(iseg,segrect);
712 offsetRect(segrect, -xp, -yp);
713 FXint ix, iw; //last (rounded) pixel
714 //coordinates used for rectangle painting
715 // ---
716 ix=0;iw=0;
717 //-- first (leftmost) column of the segment (could be clipped)
718 // (it may not need painting)
719 int lcol=seq->segs[iseg].xL-XLeft;
720 //-- last (rightmost) column (base position) of the segment
721 // which is also in the dirty rect (so needs painting)
722 int endcol=FXMIN(pRcol+1, seq->segs[iseg].xR+1-XLeft);
723
724 //-- maxcol= maximum non-clipped column (letter)
725 int maxcol=seq->segs[iseg].xR+1-XLeft-seq->segs[iseg].clipR;
726
727 //compute seg_x and seg_w to be right outside the clipping range
728 //when segrect.x and/or segrect.w are way out of drawable range
729 int seg_w = iround(segrect.w);
730 int seg_x = (int)floor(segrect.x);
731 int seg_xr = seg_x+seg_w; //right end of segment (pixels)
732 int seg_y = (int)floor(segrect.y);
733 gapRx=seg_x;
734 gapspR=seq->segs[iseg].splL;
735 bool Lend_on_screen = true;
736 bool Rend_on_screen = true;
737 if (seg_xr > dXR) {
738 //right end is outside the dirty area
739 if (seg_x > dXR) {
740 if (lastXend>INT_MIN) {
741 paintGap(dc, gapcolor, ClpRct, seg_y, lastXend, dXR, gapLx, gapRx, gapspL, gapspR);
742 lastXend=INT_MIN;
743 }
744 gapLx=(int)(segrect.x+segrect.w);
745 gapspL=seq->segs[iseg].splR;
746 continue; //skip this segment
747 }
748 Rend_on_screen=false;
749 seg_xr = dXR;
750 }
751 if (seg_x < dXL) {
752 //--left end is far away, outside the dirty area
753 if (seg_xr < dXL) {
754 lastXend=dXL;
755 gapLx=(int)(segrect.x+segrect.w);
756 gapspL=seq->segs[iseg].splR;
757 continue; //skip this segment, it's not visible
758 }
759 Lend_on_screen=false;
760 seg_x=dXL;
761 }
762 seg_w=seg_xr-seg_x;
763 int xcol=pLcol-1;
764 if (xcol<lcol) xcol=lcol;
765 //xcol = first column of the segment
766 // in the dirty rectangle
767 int xtocol=pRcol+1;
768 if (xtocol>maxcol) xtocol=maxcol;
769 ///xtocol = last non-clipped column of the segment
770 // in the directy rectangle
771 //-----------------------------------------------------
772 dc->setFont(seqfont);
773 int txtY=0;
774 if (showSeq) {
775 txtY=seg_y+iround((seqH-seqfntH)/2)+seqfont->getFontAscent()+1;
776 }
777 //-- x is set to the px coordinate of the leftmost column to be painted
778 double x=seqW*(double)xcol+CL_BORDER-xp;
779 if (lastXend>INT_MIN) {
780 if (xcol==lcol)
781 paintGap(dc, gapcolor, ClpRct, seg_y, lastXend, iround(x), gapLx, gapRx,
782 gapspL, gapspR);
783 lastXend=INT_MIN;
784 }
785 double newx=0; //-- keep adjusting newx to the next x
786 //--- draw clipL:
787 if (seq->segs[iseg].clipL>0 && lcol+seq->segs[iseg].clipL>xcol) {
788 newx=seqW*((double)(lcol+seq->segs[iseg].clipL))+CL_BORDER-xp;
789 //dc->setForeground(seqColors[4]);
790 if (iseg == 0) dc->setForeground(seqColors[4]);
791 else dc->setForeground(seqColors[9]);
792 dc->fillRectangle(iround(x), iround(segrect.y), iround(newx-x)+1,seqH);
793 //draw the text too:
794 int tlen=lcol+seq->segs[iseg].clipL-xcol;
795 if (showSeq) {
796 dc->setForeground(baseColors[0]);
797 dc->drawText((int)floor(x), txtY,
798 // seq->sequence.text()+xcol-lcol, tlen);
799 seq->sequence+seq->segs[iseg].seqofs+xcol-lcol, tlen);
800 } //letter draw
801 x=newx; // x is prepared for the next segment to be painted
802 xcol+=tlen;
803 }
804 //=== here: x and xcol are set correctly for the first used lettter
805 if (xcol<XRight-XLeft) {
806 int cth;
807 int cmsm;
808 int w;
809 int cidx;
810 int xlen=1; //run length for cth/cmsm
811 int txtlen=1;
812 int txtpos;
813 int txtcol=xcol; //same as x but for columns
814 bool mismatch=false;
815 switch (colorstyle) {
816 case FXGView::csDensity:
817 cth=columns[xcol]->thickness;
818 cmsm=columns[xcol]->mismatches;
819 cidx=iround((15.0*(cth-cmsm))/maxThickness);
820 while (xcol<xtocol) {
821 xcol++;
822 cth=columns[xcol]->thickness;
823 cmsm=columns[xcol]->mismatches;
824 newx=seqW*((double)xcol)+CL_BORDER-xp;
825 mismatch= (seq->sequence!=NULL && columns[xcol]->letter!=0 &&
826 columns[xcol]->letter!=toupper(seq->sequence[seq->segs[iseg].seqofs+xcol-lcol]));
827 int newcidx= mismatch? MISMATCH_CLRIDX : iround((15.0*(cth-cmsm))/maxThickness);
828 if (cidx!=newcidx || xcol==xtocol) {
829 //end previous run:
830 FXColor color=matchColors[cidx];
831 dc->setForeground(color);
832 ix=iround(x);
833 if (xcol==xtocol && Rend_on_screen) {
834 w=seg_xr-ix;
835 }
836 else {
837 w = iround(seqW*xlen);
838 if (w<iround(newx-x+1)) w++;
839 }
840 dc->fillRectangle(ix,segrect.iy(),w,seqH);
841 //now paint the text too in that area:
842 if (showSeq) {
843 dc->setForeground(baseColors[0]);
844 dc->drawText((int)floor(x), txtY,
845 //seq->sequence.text()+txtcol-lcol, txtlen);
846 seq->sequence+seq->segs[iseg].seqofs+txtcol-lcol, txtlen);
847 } //letter draw
848 xlen=1;
849 txtlen=1;
850 x=newx;
851 txtcol=xcol;
852 cidx=newcidx;
853 }
854 else {
855 xlen++;
856 txtlen++;
857 }
858 } //while column
859 break;
860 case FXGView::csBaseColor:
861 txtlen=xtocol - xcol;
862 txtcol=xcol;
863 txtpos=(int)floor(x);
864 while (xcol<xtocol) {
865 newx=seqW*((double)(xcol+1))+CL_BORDER-xp;
866 dc->setForeground(baseColors[nt2color(seq->sequence[xcol-lcol+seq->segs[iseg].seqofs])]);
867 w = iround(newx-x);
868 if (w<iround(newx-x+1)) w++;
869 dc->fillRectangle(iround(x),segrect.iy(), w, seqH);
870 x=newx;
871 xcol++;
872 }
873 goto DRAW_TEXT;
874 break;
875
876 case FXGView::csDefault:
877 //this also accounts for the subcoloring or ET coloring
878 txtlen=xtocol - xcol;
879 txtcol=xcol;
880 txtpos=(int)floor(x);
881 newx=seqW*((double)xtocol)+CL_BORDER-xp;
882 if (seq->segs[iseg].clipR==0) {
883 w = seg_xr-iround(x);
884 }
885 else {
886 w=iround(seqW*(xtocol-xcol));
887 if (w<iround(newx-x+1)) w++;
888 }
889 //if (w<=0) w=1;
890 if (w>0) {
891 if (seq->group>=0) {
892 dc->setForeground(grpColors[0][seq->group]);
893 }
894 else {
895 dc->setForeground(seq->isET?seqColors[7]:seqColors[0]);
896 }
897 dc->fillRectangle(iround(x),seg_y, w, seqH);
898 //seq drawing:
899 DRAW_TEXT:
900 if (showSeq) {
901 dc->setForeground(seqColors[3]);
902 dc->drawText(txtpos, txtY,
903 //seq->sequence.text()+txtcol-lcol, txtlen);
904 seq->sequence+seq->segs[iseg].seqofs+txtcol-lcol, txtlen);
905 xcol+=txtlen;
906 } //letter draw
907 }
908 x=newx;
909 break;
910 } //switch colorstyle
911 } //xcol within active range
912
913 if (Rend_on_screen) lastXend=seg_xr;
914 /* draw right end clipping ---- */
915 if (seq->segs[iseg].clipR>0 && maxcol<endcol) {
916 //dc->setForeground(seqColors[4]);
917 if (iseg == (seq->numsegs - 1)) dc->setForeground(seqColors[4]);
918 else dc->setForeground(seqColors[9]);
919 //x=seqW*((double)(maxcol))+CL_BORDER-xp);
920 //newx=seqW*((double)(endcol))+CL_BORDER-xp);
921 ix=iround(x);
922 iw=seg_xr-ix;
923 if (iw>0) {
924 dc->fillRectangle(ix, seg_y, iw ,seqH);
925 //draw the text too:
926 int tlen=endcol-maxcol;
927 if (showSeq) {//letter drawing
928 dc->setForeground(baseColors[0]);
929 //dc->drawText((int)floor(x), txtY,
930 dc->drawText((int)floor(seqW*((double)(maxcol))+CL_BORDER-xp), txtY,
931 //seq->sequence.text()+maxcol-lcol, tlen);
932 seq->sequence+seq->segs[iseg].seqofs+maxcol-lcol, tlen);
933
934 }
935 }
936 }
937 gapLx=(int)(segrect.x+segrect.w);
938 gapspL=seq->segs[iseg].splR;
939 } // segment loop
940
941 //-------
942 paintSeqBorder(dc,ClpRct,seq,colorstyle);
943
944 }
945
946 void FXGView::paintGap(FXDCWindow* dc, FXColor gapcolor, FXRectangle& ClpRct, int y, int x1, int x2,
947 int gapl, int gapr, char splL, char splR) {
948 if (x2<x1) return;
949 if (x1<ClpRct.x) x1=ClpRct.x;
950 if (x2>ClpRct.x+ClpRct.w) x2=ClpRct.x+ClpRct.w;
951 //dc->setClipRectangle(ClpRct);
952 int splh;
953 if (seqH>=2) {
954 dc->setLineWidth(2);
955 y=iround(y+seqH/2);
956 splh=seqH;
957 }
958 else {
959 dc->setLineWidth(1);
960 splh=1;
961 }
962 //dc->setForeground(seqColors[8]);
963 dc->setForeground(gapcolor);
964 dc->drawLine(x1,y, x2, y);
965 dc->setLineWidth(1);
966 int splw=iround(seqW*2.00);
967 if (splw<1) splw=1;
968 if (splh>5) splh=5;
969 int sh = (splh>>1);
970 if (splL>0) {
971 FXRectangle rL(gapl,y-sh,splw,splh);
972 FXRectangle ir=FXRIntersection(ClpRct,rL);
973 if (ir.w>0) {
974 dc->setForeground(splL=='S'?seqColors[10]:seqColors[11]);
975 dc->fillRectangle(ir.x,ir.y,ir.w,ir.h);
976 }
977 }
978 if (splR>0) {
979 FXRectangle rR(gapr-splw,y-sh,splw,splh);
980 FXRectangle ir=FXRIntersection(ClpRct,rR);
981 if (ir.w>0) {
982 dc->setForeground(splR=='S'?seqColors[10]:seqColors[11]);
983 dc->fillRectangle(ir.x,ir.y,ir.w,ir.h);
984 }
985 }
986 }
987
988 void FXGView::paintSeqBorder(FXDCWindow* dc, FXRectangle& ClpRct, ClSeq* seq,
989 ColorStyle colorstyle) {
990 int xp = -getXPosition();
991 int yp = -getYPosition();
992 //int endx=ClpRct.x + ClpRct.w+1; //end X coordinate
993 CRect seqrect;
994 seq->calcViewRect(seqrect);
995 offsetRect(seqrect, -xp, -yp);
996
997 int margin=CL_BORDER<<1;
998 //compute seq_x and seq_w to be right outside the clipping range
999 //when seqrect.x and/or seqrect.w are way out of drawable range
1000 bool Lend_on_screen = true;
1001 bool Rend_on_screen = true;
1002 int seq_w = iround(seqrect.w);
1003 int seq_x = (int)floor(seqrect.x);
1004 int seq_xr = seq_x+seq_w;
1005 int seq_y = (int)floor(seqrect.y);
1006 if (seq_xr > (int)ClpRct.x+(int)ClpRct.w+margin) {
1007 //right end is far away, outside the clipping area
1008 Rend_on_screen=false;
1009 seq_xr = (int)ClpRct.x+(int)ClpRct.w+margin;
1010 }
1011 if (seq_x < (int)ClpRct.x-margin) {
1012 //left end is far away, outside the clipping area
1013 Lend_on_screen=false;
1014 seq_x=(int)ClpRct.x-margin;
1015 }
1016 seq_w=seq_xr-seq_x;
1017 //
1018 //FXColor color=seqColors[4];
1019 FXColor color=0xFF00F000;
1020 if (seqW>=CL_BORDER) { //draw triangle outside, we have room for that
1021 if (seq->reversed==0) {
1022 //draw on the right
1023 if (Rend_on_screen)
1024 drawTriangle(*dc, color,
1025 (int)(seqrect.x+seqrect.w), seq_y-1,
1026 (int)(seqrect.x+seqrect.w)+CL_BORDER, seq_y+seqH);
1027 }
1028 else {
1029 //draw on the left
1030 if (Lend_on_screen)
1031 drawTriangle(*dc, color,
1032 seqrect.ix(), seq_y-1,
1033 seqrect.ix()-CL_BORDER, seq_y+seqH);
1034 }
1035 }
1036 else { //scaled down: draw the triangles inside
1037 int tw=iround(seqW);
1038 if (tw<3)
1039 tw=3; //triangle width
1040 if (seqH>3 && seqrect.w>3) { //otherwise don't draw anything, it's too small scale
1041 if (seq->reversed==0) {
1042 //draw on the right
1043 if (Rend_on_screen)
1044 drawTriangle(*dc, color,
1045 (int)(seqrect.x+seqrect.w-tw)-2, seq_y+2,
1046 (int)(seqrect.x+seqrect.w)-2, seq_y+seqH-2);
1047 }
1048 else { //draw on the left
1049 if (Lend_on_screen)
1050 drawTriangle(*dc, color,
1051 seqrect.ix()+tw+2, seq_y+2,
1052 seqrect.ix()+2, seq_y+seqH-2);
1053 }
1054 }
1055 }
1056
1057 //border bevel/selection drawing
1058 //int endX = Rend_on_screen ? seq_x+seq_w:xlast;
1059 FXRectangle seqclp(seq_x, seq_y, seq_w, seqH);
1060 seqclp=FXRIntersection(seqclp, ClpRct);
1061 bool seqclpSet=false;
1062 /*
1063 if (seq->group>=0 && colorstyle==csDefault) { //group border coloring
1064 dc->setClipRectangle(seqclp);
1065 seqclpSet=true;
1066 dc->setLineWidth(4);
1067 dc->setForeground(makeHiliteColor(grpColors[seq->group]));
1068 dc->drawLine(seq_x,seq_y, seq_x+seq_w, seq_y);
1069 //left end
1070 if (Lend_on_screen)
1071 dc->drawLine(seq_x, seq_y, seq_x, seq_y+seqH);
1072 dc->setForeground(makeShadowColor(grpColors[seq->group]));
1073 dc->drawLine(seq_x,seq_y+seqH, seq_x+seq_w, seq_y+seqH);
1074 if (Rend_on_screen)
1075 dc->drawLine(seq_x+seq_w, seq_y, seq_x+seq_w, seq_y+seqH);
1076 dc->setLineWidth(1);
1077 } */
1078 // else if (colorstyle==csDefault) {
1079 /*dc->setClipRectangle(seqclp);
1080 seqclpSet=true;
1081 dc->setLineWidth(2);
1082 dc->setForeground(seqColors[1]);
1083 dc->drawLine(seq_x,seq_y, seq_x+seq_w, seq_y);
1084 //left end
1085 if (Lend_on_screen)
1086 dc->drawLine(seq_x, seq_y, seq_x, seq_y+seqH);
1087 dc->setForeground(seqColors[2]);
1088 dc->drawLine(seq_x,seq_y+seqH, seq_x+seq_w, seq_y+seqH);
1089 if (Rend_on_screen)
1090 dc->drawLine(seq_x+seq_w, seq_y, seq_x+seq_w, seq_y+seqH);
1091 dc->setLineWidth(1);
1092 */
1093 // }
1094 if (selSeq==seq) {//draw selection rectangle
1095 if (!seqclpSet) {
1096 dc->setClipRectangle(seqclp);
1097 seqclpSet=true;
1098 }
1099 dc->setForeground(seqColors[6]);
1100 dc->setLineWidth(4);
1101 dc->drawRectangle(seq_x, seq_y, seq_w, seqH);
1102 dc->setLineWidth(1);
1103 }
1104
1105 if (seqclpSet)
1106 dc->setClipRectangle(ClpRct);
1107 }
1108
1109
1110 void FXGView::paintGrid(FXDCWindow* dc, FXRectangle& gridR) {
1111 dc->setForeground(gridColor);
1112 dc->fillRectangle(gridR.x,gridR.y,
1113 gridR.w,gridR.h);
1114 int endx=gridR.x + gridR.w+1;
1115 int xp = -getXPosition();
1116 //gridStep (in columns) must be computed after each zooming
1117 int xcol=(int)floor(((double)(gridR.x+xp-CL_BORDER))/seqW);
1118 int startcol=xcol-gridStep;
1119 int xtocol=(int)floor(((double)(endx+xp-CL_BORDER))/seqW);
1120 int endcol=xtocol+gridStep;
1121 for (; startcol<endcol && startcol%gridStep !=0 ; startcol++);
1122
1123 int hf=showContig ? font->getFontHeight()+4: gridH;
1124
1125 if (startcol<endcol) { //there is something to redraw here:
1126 dc->setFont(font);
1127 dc->setLineWidth(1);
1128 for (double x=seqW*(double)startcol+CL_BORDER-xp; x<=endx; x+=seqW*gridStep) {
1129 int col = iround(((double)(x+xp-CL_BORDER))/seqW)+XLeft;
1130 dc->setForeground(gridColorH);
1131 int xx=iround(x);
1132 dc->drawLine(xx+1,hf-4,xx+1,hf);
1133 dc->setForeground(gridColorSh);
1134 dc->drawLine(xx,hf-4,xx,hf);
1135 FXString s;
1136 s.format("%d",col);
1137 dc->drawText((int)x, hf-6, s.text(), s.length());
1138 }
1139 }
1140 //draw contig if there, with quality values:
1141 if (showContig && xcol<XRight-XLeft) { //xcol is the starting column
1142 xcol--;
1143 if (xcol<0) xcol=0;
1144 xtocol++;
1145 if (xtocol>=columns.Count()) xtocol=columns.Count()-1;
1146 double x=seqW*(double)xcol+CL_BORDER-xp;
1147 int cth=columns[xcol]->thickness;
1148 int cmsm=columns[xcol]->mismatches;
1149 int cidx=(xcol<contig->cl_xpos-XLeft || xcol>contig->cl_xpos-XLeft+contig->len-1) ?
1150 NOCTG_CLRIDX : iround((15.0*(cth-cmsm))/maxThickness);
1151 int xlen=1; //run length for cth/cmsm
1152 dc->setFont(seqfont);
1153 int txtcol=xcol; //same as x but for columns
1154 int txtlen=1;
1155 while (xcol<xtocol) {
1156 xcol++;
1157 cth=columns[xcol]->thickness;
1158 cmsm=columns[xcol]->mismatches;
1159 double newx=seqW*((double)xcol)+CL_BORDER-xp;
1160 int newcidx=(xcol<contig->cl_xpos-XLeft || xcol>contig->cl_xpos-XLeft+contig->len-1) ?
1161 NOCTG_CLRIDX : iround((15.0*(cth-cmsm))/maxThickness);
1162 if (cidx!=newcidx || xcol==xtocol) {
1163 // change of color; draw the previous rectangle
1164 FXColor color=ctgQuals[cidx];
1165 dc->setForeground(color);
1166 int w = iround(seqW*xlen);
1167 if (w<iround(newx-x+1)) w++;
1168 dc->fillRectangle(iround(x),gridH-seqH,w,seqH-2);
1169 //now paint the text too in that area:
1170 if (cidx!=NOCTG_CLRIDX && canShowSeq && contig->sequence!=NULL) {
1171 dc->setForeground(FXRGB(0x00, 0x00, 0x00));
1172 dc->drawText((int)floor(x), gridH-2,
1173 contig->sequence+txtcol-(contig->cl_xpos-XLeft), txtlen);
1174 } //letter draw
1175 x=newx;
1176 txtcol=xcol;
1177 xlen=1;
1178 txtlen=1;
1179 cidx=newcidx;
1180 }
1181 else {
1182 xlen++;
1183 txtlen++;
1184 }
1185 } //while column
1186 //now for the last segment:
1187 } //if showcontig
1188 dc->setLineWidth(1);
1189 dc->setForeground(makeShadowColor(gridColor));
1190 dc->drawLine(gridR.x, gridH-1,
1191 gridR.x+gridR.w, gridH-1);
1192 dc->setForeground(0xFF000000);
1193 dc->drawLine(gridR.x, gridH,
1194 gridR.x+gridR.w, gridH);
1195 }
1196
1197 // Draw item list
1198 long FXGView::onPaint(FXObject*,FXSelector,void* ptr){
1199 FXEvent* event=(FXEvent*)ptr;
1200 //event holds the coordinates for the invalidated rectangle only!
1201 //avoiding unneccessary repainting of the whole canvas
1202 //cool!
1203 FXDCWindow dc(this,event);
1204 FXRectangle clip(event->rect);
1205 FXRectangle gridR(0,0,viewport_w+1, gridH+1);
1206 gridR=FXRIntersection(clip,gridR);
1207 //rectangle to be converted to global, "content" coordinates
1208 //back buffer stuff
1209 FXDCWindow *bufdc=new FXDCWindow(backbuf);
1210 if (gridR.h>0) {
1211 //paint the top grid:
1212 //gridR is 0,0 based
1213 paintGrid(bufdc, gridR);
1214 clip.h=clip.y+clip.h-gridH;
1215 clip.y=gridH+1;
1216 }
1217
1218 if (rows.Count()>0) {
1219 /*
1220 if (clip.y<gridH) {//?!?!? strange bug sometimes this is just out of range (negative coords!)
1221 //GMessage("gridR non empty (gridR.h=%d, cliprect set to (%d, %d, %d, %d)\n",
1222 // gridR.h, clip.x, clip.y, clip.w, clip.h);
1223 //clip.y=gridH+1;
1224 }
1225 */
1226 bufdc->setClipRectangle(clip);
1227
1228 bufdc->setForeground(backColor);
1229 bufdc->fillRectangle(clip.x,clip.y,
1230 clip.w,clip.h);
1231 //paint this by row - only visible rows
1232 //locate first and last visible rows:
1233
1234 CRect rect(clip);
1235 //rect is offset to be positioned in the whole content space:
1236 offsetRect(rect,-getXPosition(),-getYPosition());
1237 //start-end columns to check
1238 int c1=(int)floor(((double)(rect.x-CL_BORDER))/seqW)-1;
1239 int c2=(int)floor(((double)(rect.x+rect.w-CL_BORDER))/seqW)+1;
1240
1241 int r1=iround((rect.y-gridH-CL_BORDER)/(seqH+vSpace))-1;
1242 if (r1<0) r1=0;
1243 int r2=iround((rect.y+rect.h-gridH-CL_BORDER)/(seqH+vSpace))+1;
1244 if (r2>=rows.Count()-1) r2=rows.Count()-1;
1245 ColorStyle cs=colorStyle;
1246 if (seqW<1)
1247 cs=FXGView::csDefault;
1248 for (int i=r1; i<=r2; i++)
1249 for (int j=0;j<rows[i]->seqs.Count(); j++) {
1250 ClSeq* sq=rows[i]->seqs[j];
1251 if (sq->cl_xpos-XLeft>c2) continue;
1252 if (sq->cl_xpos+sq->len-XLeft<c1) continue;
1253 paintSeq(bufdc,clip, sq, cs);
1254 }
1255 if (selCol>=0) { //draw column selection
1256 bufdc->setClipRectangle(event->rect);
1257 int cx= iround(selCol*seqW)+CL_BORDER+getXPosition();
1258 bufdc->setLineWidth(1);
1259 bufdc->setFillStyle(FILL_OPAQUESTIPPLED);
1260 bufdc->setStipple(STIPPLE_GRAY,cx,getYPosition());
1261 //bufdc->setFunction((FXFunction)BLT_SRC_XOR_DST);
1262 bufdc->setForeground(getTextColor());
1263 bufdc->setBackground(seqColors[6]);
1264 bufdc->drawLine(cx,0, cx, viewport_h );
1265 bufdc->drawLine(cx+(int)seqW, 0, cx+(int)seqW, viewport_h);
1266 //bufdc->drawRectangle(cx,0,seqW,viewport_h);
1267 bufdc->setFillStyle(FILL_SOLID);
1268 bufdc->setStipple(STIPPLE_NONE);
1269 }
1270 delete bufdc; //? This is needed to actually flush the changes!?!?
1271 }
1272 else {
1273 dc.setForeground(backColor);
1274 dc.fillRectangle(clip.x,clip.y,
1275 clip.w,clip.h);
1276 }
1277 //now draw the buffer:
1278 // Set font
1279 //dc.setForeground(fxcolorfromname("LightGray"));
1280 //dc.fillRectangle(0,0,
1281 // getWidth(),getHeight());
1282 //dc.drawImage(backbuf,event->rect.x,event->rect.y);
1283 dc.drawArea(backbuf, event->rect.x,event->rect.y, event->rect.w,event->rect.h,
1284 event->rect.x,event->rect.y);
1285 return 1;
1286 }
1287
1288 FXint FXGView::getContentWidth() {
1289 //if(flags&FLAG_RECALC) recompute();
1290 return content_w;
1291 }
1292
1293 // Determine content height of list
1294 FXint FXGView::getContentHeight(){
1295 //if(flags&FLAG_RECALC) recompute();
1296 return content_h;
1297 }
1298
1299 // Change the font
1300 void FXGView::setFont(FXFont* fnt){
1301 if(!fnt){ fxerror("%s::setFont: NULL font specified.\n",getClassName()); }
1302 if(font!=fnt){
1303 font=fnt;
1304 recalc();
1305 update();
1306 }
1307 }
1308
1309 // Set text color
1310 void FXGView::setTextColor(FXColor clr){
1311 if(clr!=textColor){
1312 textColor=clr;
1313 update();
1314 }
1315 }
1316
1317 // Save data
1318 void FXGView::save(FXStream& store) const {
1319 FXScrollArea::save(store);
1320 store << font;
1321 store << textColor;
1322 }
1323 // Load data
1324 void FXGView::load(FXStream& store){
1325 FXScrollArea::load(store);
1326 store >> font;
1327 store >> textColor;
1328 }
1329
1330 //-- compare functions for sequences
1331 int compareXPos(void* p1, void* p2) {
1332 int c1=((ClSeq*)p1)->cl_xpos;
1333 int c2=((ClSeq*)p2)->cl_xpos;
1334 return (c1>c2)?1:((c1<c2)?-1:0);
1335 }
1336
1337 int compareXRoom(void* p1, void* p2) {
1338 int c1=((ClSeq*)p1)->view->XRight-((ClSeq*)p1)->cl_xpos+((ClSeq*)p1)->len;
1339 int c2=((ClSeq*)p2)->view->XRight-((ClSeq*)p2)->cl_xpos+((ClSeq*)p2)->len;
1340 return (c1>c2)?-1:((c1<c2)?1:0);
1341 }
1342
1343 int compareYPos(void* p1, void* p2) {
1344 int c1=((ClSeq*)p1)->cl_ypos;
1345 int c2=((ClSeq*)p2)->cl_ypos;
1346 return (c1>c2)?1:((c1<c2)?-1:0);
1347 }
1348
1349 void FXGView::addContig(const char* name, FXint len,
1350 const char* sequence, FXint offs) {
1351 contig=new ClSeq(this, name,len, offs, false, 0, 0, sequence);
1352 //=addSeq(name, len, offs, false, 0,0,sequence);
1353 if (sequence!=NULL) hasSeqs=true;
1354 contig->cl_ypos=0;
1355 if (seqlist->Count()==0) {
1356 XLeft=offs;
1357 XRight=contig->cl_xpos+contig->len;
1358 }
1359 else {
1360 if (offs<XLeft) XLeft=offs;
1361 //also re-check current dimension
1362 if (contig->cl_xpos + contig->len > XRight)
1363 XRight=contig->cl_xpos+contig->len; //extreme right position
1364 }
1365 }
1366
1367 ClSeq* FXGView::addSeq(const char* name, FXint len, FXint offs,
1368 bool reversed, int trim_l, int trim_r, const char* sequence,
1369 unsigned int ins, unsigned int dels) {
1370 ClSeq* seq=new ClSeq(this, name,len, offs, reversed, trim_l, trim_r, sequence,
1371 ins, dels);
1372 if (sequence!=NULL) hasSeqs=true;
1373 seqlist->Add(seq);
1374 //for now:
1375 seq->cl_ypos=seqlist->Count();
1376 if (seqlist->Count()==1) {
1377 XLeft=offs;
1378 XRight=seq->cl_xpos+seq->len;
1379 }
1380 else {
1381 if (offs<XLeft) XLeft=offs;
1382 //also re-check current dimension
1383 if (seq->cl_xpos + seq->len > XRight)
1384 XRight=seq->cl_xpos+seq->len; //extreme right position
1385 }
1386 return seq;
1387 //RecalcContent();
1388 }
1389
1390 ClSeq* FXGView::addSeq(LytSeqInfo* seqinfo, const char* sequence) {
1391 ClSeq* seq=new ClSeq(this, seqinfo, sequence);
1392 if (sequence!=NULL) hasSeqs=true;
1393 seqlist->Add(seq);
1394 //--------
1395 seq->cl_ypos=seqlist->Count();
1396 if (seqlist->Count()==1) {
1397 XLeft=seqinfo->offs;
1398 XRight=seq->cl_xpos+seq->len;
1399 }
1400 else {
1401 if (seqinfo->offs<XLeft) XLeft=seqinfo->offs;
1402 //also re-check current dimension
1403 if (seq->cl_xpos + seq->len > XRight)
1404 XRight=seq->cl_xpos+seq->len; //extreme right position
1405 }
1406 return seq;
1407 //RecalcContent();
1408 }
1409
1410
1411
1412 void FXGView::RecalcContent(bool re_paint) {
1413 seqH = iround((scale.sy * (double)seqfntH));
1414 if (seqH==0) seqH=1;
1415 vSpace=seqH/4;
1416 seqW = (scale.sx * (double)seqfntW);
1417 if (seqW==0) seqW=0.01;
1418 canShowSeq = (seqfntW<=seqW && seqfntH<=seqH);
1419 gridH=font->getFontHeight()+4;
1420 if (contig!=NULL) {
1421 gridH+=seqH+2;
1422 showContig=true;
1423 }
1424 else showContig=false;
1425 //getFontWidth() doesn't really work for ps fonts,
1426 //so I use 40 as the max width of a grid number in pixels
1427 for (gridStep=10; seqW*gridStep < 40; gridStep+=10) ;
1428 //recompute the sequence height and vspace, based on scale.sy:
1429 int minH=(seqH+vSpace)*rows.Count()+gridH+CL_BORDER*2;
1430 int minW=iround(seqW*(double)(XRight-XLeft))+2*CL_BORDER;
1431 if (content_h!=minH+4) content_h=minH+4;
1432 if (content_w!=minW+4) content_w=minW+4;
1433 vertical->setLine(seqH+vSpace);
1434 horizontal->setLine((int)seqW);
1435 if (re_paint) {
1436 layout();
1437 update();
1438 }
1439 }
1440
1441 int qsortcmp(const void* p1, const void* p2) {
1442 NTColumn* c1 =(NTColumn*)p1;
1443 NTColumn* c2 =(NTColumn*)p2;
1444 return (c1->count>c2->count)? -1 : ((c1->count<c2->count)?1:0);
1445 }
1446
1447 void FXGView::buildLayout() {
1448 //try to place more than one sequence on a row
1449 seqlist->setSorted(compareXPos);
1450 rows.Clear();
1451 columns.Clear();
1452 for (int i=0;i<seqlist->Count();i++) {
1453 ClSeq* seq = seqlist->Get(i);
1454 //search back for possible room
1455 int r=-1;
1456 for (int j=0;j<rows.Count();j++)
1457 if (seq->cl_xpos-2 > rows[j]->rpos) {
1458 r=j;
1459 break;
1460 }
1461 if (r>=0)
1462 rows[r]->addSeq(seq, r);
1463 else
1464 rows.Add(new CLayoutRow(seq,rows.Count()));
1465 }
1466
1467 //-- analyze columns -----:
1468 for (int c=XLeft; c<=XRight; c++) {
1469 ColumnData* column=new ColumnData();
1470 column->ntdata[0].letter='A';column->ntdata[0].count=0;
1471 column->ntdata[1].letter='C';column->ntdata[1].count=0;
1472 column->ntdata[2].letter='G';column->ntdata[2].count=0;
1473 column->ntdata[3].letter='T';column->ntdata[3].count=0;
1474 column->ntdata[4].letter='N';column->ntdata[4].count=0;
1475 columns.Add(column);
1476 }
1477 //populate column data based on row content;
1478 for (int r=0;r<rows.Count();r++) {
1479 for (int i = 0; i < rows[r]->seqs.Count(); i++) { //for each read
1480 ClSeq* cs=rows[r]->seqs[i];
1481 if (hasSeqs) {
1482 // for each segment
1483 for (int j = 0; j < cs->numsegs; j++) {
1484 for (int k=cs->segs[j].xL;k<=cs->segs[j].xR;k++) {
1485 ColumnData* column = columns[k-XLeft];
1486 column->thickness++;
1487 char c=toupper(cs->sequence[k-cs->segs[j].xL+cs->segs[j].seqofs]);
1488 switch (c) {
1489 case 0:break;
1490 case ' ':column->thickness++;
1491 break; //no sequence given
1492 case 'A':
1493 case 'a':column->ntdata[0].count++;column->thickness++;break;
1494 case 'C':
1495 case 'c':column->ntdata[1].count++;column->thickness++;break;
1496 case 'G':
1497 case 'g':column->ntdata[2].count++;column->thickness++;break;
1498 case 'T':
1499 case 't':column->ntdata[3].count++;column->thickness++;break;
1500 case '*':
1501 case '-': column->gap++;
1502 column->thickness++;
1503 break;
1504 default: { column->ntdata[4].count++; column->nN++; column->thickness++; }
1505 } //switch
1506 }
1507 }
1508 } // hasSeqs
1509 else {
1510 for (int j = cs->cl_xpos; j < cs->cl_xpos+cs->len; j++) {
1511 columns[j-XLeft]->thickness++;
1512 }
1513 } //no seq
1514 }//for each read
1515 }
1516
1517 maxThickness=0;
1518 for (int c=XLeft; c<=XRight; c++) {
1519 ColumnData* column=columns[c-XLeft];
1520 if (column->thickness > maxThickness)
1521 maxThickness=column->thickness;
1522 //decide what's the letter to use:
1523 if (hasSeqs) {
1524 char letter='\0';
1525 column->letter='\0';
1526 int max=0;
1527 if (column->thickness > 0) {
1528 qsort(column->ntdata,4, sizeof(NTColumn), qsortcmp);
1529 max=FXMAX(column->ntdata[0].count, column->nN);
1530 if (contig!=NULL && contig->sequence!=NULL && c>=contig->cl_xpos) {
1531 column->letter=toupper(contig->sequence[c-contig->cl_xpos]);
1532 //fprintf(stderr, "%c",column->letter);
1533 }
1534 }
1535 if (column->letter) {
1536 column->mismatches=0;
1537 bool wasletter=false;
1538 for (int nt=0;nt<5;nt++) {
1539 if (column->ntdata[nt].letter!=column->letter && column->ntdata[nt].letter!='N')
1540 column->mismatches+=column->ntdata[nt].count;
1541 if (column->ntdata[nt].letter==column->letter && column->ntdata[nt].letter!='N')
1542 wasletter=true;
1543 }
1544 if (column->letter=='*' || column->letter=='-')
1545 column->mismatches=column->thickness - column->gap;
1546 else if (!wasletter)
1547 column->mismatches=column->thickness - column->nN;
1548
1549 if (column->mismatches<0) {
1550 fprintf(stderr, "Warning: Column %d: %d mism, thickness=%d, gap=%d, nN=%d (%c)\n",
1551 c, column->mismatches, column->thickness, column->gap, column->nN, wasletter?'1':'0');
1552
1553 for (int nt=0;nt<5;nt++) {
1554 fprintf(stderr, "letter %c = %d\n", column->ntdata[nt].letter, column->ntdata[nt].count);
1555 }
1556 }
1557 }
1558 else {
1559 if (column->gap>max) {
1560 letter = '*';
1561 column->mismatches = column->thickness - column->gap;
1562 }
1563 else {
1564 letter = column->ntdata[0].letter;
1565 column->mismatches=column->thickness - column->ntdata[0].count;
1566 }
1567 }
1568 }//hasSeqs
1569 } //for each column
1570
1571
1572 /*
1573 maxThickness=0;
1574 for (int c=XLeft; c<=XRight; c++) {
1575 int gap=0, nN=0;
1576 ColumnData* column=new ColumnData();
1577 //TODO: use IUPAC ambiguity codes all over
1578 column->ntdata[0].letter='A';column->ntdata[0].count=0;
1579 column->ntdata[1].letter='C';column->ntdata[1].count=0;
1580 column->ntdata[2].letter='G';column->ntdata[2].count=0;
1581 column->ntdata[3].letter='T';column->ntdata[3].count=0;
1582 column->ntdata[4].letter='N';column->ntdata[4].count=0;
1583 for (int r=0;r<rows.Count();r++) {
1584 switch (rows[r]->getNuc(c)) {
1585 case 0:break;
1586 case ' ':column->thickness++;
1587 break; //no sequence given
1588 case 'A':
1589 case 'a':column->ntdata[0].count++;column->thickness++;break;
1590 case 'C':
1591 case 'c':column->ntdata[1].count++;column->thickness++;break;
1592 case 'G':
1593 case 'g':column->ntdata[2].count++;column->thickness++;break;
1594 case 'T':
1595 case 't':column->ntdata[3].count++;column->thickness++;break;
1596 case '*':
1597 case '-': gap++;
1598 column->thickness++;
1599 break;
1600 default: { column->ntdata[4].count++; nN++; column->thickness++; }
1601 } //switch
1602 } //for each row
1603 if (column->thickness > maxThickness)
1604 maxThickness=column->thickness;
1605 //decide what's the letter to use:
1606 if (hasSeqs) {
1607 char letter='\0';
1608 column->letter='\0';
1609 int max=0;
1610 if (column->thickness > 0) {
1611 qsort(column->ntdata,4, sizeof(NTColumn), qsortcmp);
1612 max=FXMAX(column->ntdata[0].count, nN);
1613 if (contig!=NULL && !contig->sequence.empty() && c>=contig->cl_xpos) {
1614 column->letter=toupper(contig->sequence[c-contig->cl_xpos]);
1615 //fprintf(stderr, "%c",column->letter);
1616 }
1617 }
1618 if (column->letter) {
1619 column->mismatches=0;
1620 bool wasletter=false;
1621 for (int nt=0;nt<5;nt++) {
1622 if (column->ntdata[nt].letter!=column->letter && column->ntdata[nt].letter!='N')
1623 column->mismatches+=column->ntdata[nt].count;
1624 if (column->ntdata[nt].letter==column->letter && column->ntdata[nt].letter!='N')
1625 wasletter=true;
1626 }
1627 if (column->letter=='*' || column->letter=='-')
1628 column->mismatches=column->thickness - gap;
1629 else if (!wasletter)
1630 column->mismatches=column->thickness - nN;
1631
1632 if (column->mismatches<0) {
1633 fprintf(stderr, "Warning: Column %d: %d mism, thickness=%d, gap=%d, nN=%d (%c)\n",
1634 c, column->mismatches, column->thickness, gap, nN, wasletter?'1':'0');
1635
1636 for (int nt=0;nt<5;nt++) {
1637 fprintf(stderr, "letter %c = %d\n", column->ntdata[nt].letter, column->ntdata[nt].count);
1638 }
1639 }
1640 }
1641 else {
1642 if (gap>max) {
1643 letter = '*';
1644 column->mismatches = column->thickness - gap;
1645 }
1646 else {
1647 letter = column->ntdata[0].letter;
1648 column->mismatches=column->thickness - column->ntdata[0].count;
1649 }
1650 }
1651 }
1652 columns.Add(column);
1653 }//for each column
1654 */
1655
1656 //sort seqlist by name!!! very important for group loading
1657 seqlist->setSorted(true);
1658 RecalcContent();
1659 }
1660
1661
1662 void FXGView::ZoomY(FXdouble zy, int fromY) {
1663 //save the row num of the center of the screen
1664 if (zy<=0) zy=0.01;
1665 int yscroll = getYPosition();
1666 int cy;
1667 if (fromY<0) {
1668 if (selSeq!=NULL) {
1669 //zoom about the select sequence row
1670 cy=gridH+CL_BORDER+(seqH+vSpace)*selSeq->cl_ypos;
1671 }
1672 else cy=height/2 - yscroll;
1673 }
1674 else
1675 cy=fromY-yscroll;
1676
1677 double r0=((double)(cy - gridH-CL_BORDER))/(seqH+vSpace);
1678 scale.sy = zy;
1679 RecalcContent(false);
1680 //new coordinates:
1681 double r1=(double)((cy - gridH-CL_BORDER))/(seqH+vSpace);
1682 calcLayout();
1683 //try to offset the view with the number of rows
1684 vertical->setPosition(-(yscroll+iround((r1-r0)*(seqH+vSpace))));
1685 pos_y=-vertical->getPosition();
1686 update();
1687 }
1688
1689 void FXGView::ZoomX(FXdouble zx, int fromX) {
1690 if (zx<=0) zx=0.001;
1691 int xscroll = getXPosition();
1692 int cx;
1693 if (fromX<0) { //zoom using a default center
1694 if (selCol>=0) {
1695 //zoom about the intersection nucleotide
1696 cx=CL_BORDER+(int)floor(seqW*(selCol+1));
1697 }
1698 else
1699 cx=width/2 - xscroll;
1700 }
1701 else
1702 cx=fromX-xscroll; //given center;
1703 double c0=((double)(cx - CL_BORDER))/seqW;
1704 scale.sx = zx;
1705 RecalcContent(false);
1706 //new coordinates:
1707 double c1=(double)((cx - CL_BORDER))/seqW;
1708 //try to offset the view back to it's original position
1709 calcLayout();
1710 horizontal->setPosition(-(xscroll+iround(((c1-c0)*seqW))));
1711 pos_x=-horizontal->getPosition();
1712 update();
1713 }
1714
1715 /* Move content */
1716 void FXGView::moveContents(FXint x,FXint y){
1717 FXint dx=x-pos_x;
1718 FXint dy=y-pos_y;
1719 pos_x=x;
1720 pos_y=y;
1721 scroll(0,gridH+1,viewport_w,viewport_h-gridH,dx,dy);
1722 if (dx!=0) { //scroll grid area too
1723 scroll(0,0,viewport_w,gridH,dx,0);
1724 }
1725 }
1726
1727 void FXGView::Zoom(FXdouble zx, FXdouble zy, int fromX, int fromY) {
1728 if (zy<=0) zy=0.01;
1729 if (zx<=0) zx=0.001;
1730 //if (zx<0.01 || zx>4 || zy<0.01 || zy>4) return;
1731 int yscroll = getYPosition();
1732 int xscroll = getXPosition();
1733 int cx;
1734 if (fromX<0) { //zoom using a default center
1735 if (selCol>=0) {
1736 //zoom about the intersection nucleotide
1737 cx=CL_BORDER+(int)floor(seqW*(selCol+1));
1738 }
1739 else
1740 cx=width/2 - xscroll;
1741 }
1742 else
1743 cx=fromX-xscroll; //given center;
1744 int cy;
1745 if (fromY<0) {
1746 if (selSeq!=NULL) {
1747 //zoom about the intersection nucleotide
1748 cy=gridH+CL_BORDER+(seqH+vSpace)*selSeq->cl_ypos;
1749 }
1750 else cy=height/2 - yscroll;
1751 }
1752 else
1753 cy=fromY-yscroll;
1754 double r0=((double)(cy - gridH-CL_BORDER))/(seqH+vSpace);
1755 scale.sy = zy;
1756 double c0=((double)(cx - CL_BORDER))/seqW;
1757 scale.sx = zx;
1758 RecalcContent(false);
1759 //new coordinates:
1760 double r1=(double)((cy - gridH - CL_BORDER))/(seqH+vSpace);
1761 double c1=(double)((cx - CL_BORDER))/seqW;
1762 //try to offset the view with the number of rows
1763 calcLayout();
1764 vertical->setPosition(-(yscroll+iround((r1-r0)*(seqH+vSpace))));
1765 pos_y=-vertical->getPosition();
1766 horizontal->setPosition(-(xscroll+iround(((c1-c0)*seqW))));
1767 pos_x=-horizontal->getPosition();
1768 update();
1769 }
1770
1771 //same as FXScrollArea::layout() but without repainting...
1772 void FXGView::calcLayout() {
1773 register FXint new_x,new_y;
1774 register FXint sh_h=0;
1775 register FXint sv_w=0;
1776 // Inviolate
1777 FXASSERT(pos_x<=0 && pos_y<=0);
1778 // Initial viewport size
1779 viewport_w=getViewportWidth();
1780 viewport_h=getViewportHeight();
1781 // ALWAYS determine content size
1782 content_w=getContentWidth();
1783 content_h=getContentHeight();
1784
1785 // Get dimensions of the scroll bars
1786 if(!(options&HSCROLLER_NEVER)) sh_h=horizontal->getDefaultHeight();
1787 if(!(options&VSCROLLER_NEVER)) sv_w=vertical->getDefaultWidth();
1788
1789 // Should we disable the scroll bars?
1790 // A bit tricky as the scrollbars may influence each other's presence
1791 if(!(options&(HSCROLLER_ALWAYS|VSCROLLER_ALWAYS)) && (content_w<=viewport_w) && (content_h<=viewport_h)){sh_h=sv_w=0;}
1792 if(!(options&HSCROLLER_ALWAYS) && (content_w<=viewport_w-sv_w)) sh_h=0;
1793 if(!(options&VSCROLLER_ALWAYS) && (content_h<=viewport_h-sh_h)) sv_w=0;
1794 if(!(options&HSCROLLER_ALWAYS) && (content_w<=viewport_w-sv_w)) sh_h=0;
1795
1796 // Viewport size with scroll bars taken into account
1797 viewport_w-=sv_w;
1798
1799 viewport_h-=sh_h;
1800
1801 // Adjust content size, now that we know about those scroll bars
1802 if((options&HSCROLLER_NEVER)&&(options&HSCROLLER_ALWAYS)) content_w=viewport_w;
1803 if((options&VSCROLLER_NEVER)&&(options&VSCROLLER_ALWAYS)) content_h=viewport_h;
1804
1805 // Furthermore, content size won't be smaller than the viewport
1806 if(content_w<viewport_w) content_w=viewport_w;
1807 if(content_h<viewport_h) content_h=viewport_h;
1808
1809 // Content size
1810 horizontal->setRange(content_w);
1811 vertical->setRange(content_h);
1812
1813 // Page size may have changed
1814 horizontal->setPage(viewport_w);
1815 vertical->setPage(viewport_h);
1816
1817 // Position may have changed
1818 horizontal->setPosition(-pos_x);
1819 vertical->setPosition(-pos_y);
1820
1821 // Get back the adjusted position
1822 new_x=-horizontal->getPosition();
1823 new_y=-vertical->getPosition();
1824 /*
1825 // Scroll to force position back into range
1826 if(new_x!=pos_x || new_y!=pos_y){
1827 moveContents(new_x,new_y);
1828 }
1829 */
1830 // Read back validated position
1831 pos_x=-horizontal->getPosition();
1832 pos_y=-vertical->getPosition();
1833
1834 // Hide or show horizontal scroll bar
1835 if(sh_h){
1836 horizontal->position(0,height-sh_h,width-sv_w,sh_h);
1837 horizontal->show();
1838 horizontal->raise();
1839 }
1840 else{
1841 horizontal->hide();
1842 }
1843 // Hide or show vertical scroll bar
1844 if(sv_w){
1845 vertical->position(width-sv_w,0,sv_w,height-sh_h);
1846 vertical->show();
1847 vertical->raise();
1848 }
1849 else{
1850 vertical->hide();
1851 }
1852 // Hide or show scroll corner
1853 if(sv_w && sh_h){
1854 corner->position(width-sv_w,height-sh_h,sv_w,sh_h);
1855 corner->show();
1856 corner->raise();
1857 }
1858 else{
1859 corner->hide();
1860 }
1861 }