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 (10 years, 10 months ago) by gpertea
File size: 58292 byte(s)
Log Message:
added gfview files

Line User Rev File contents
1 gpertea 45 #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     }