NCBI C++ ToolKit
sequence_viewer_widget.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: sequence_viewer_widget.cpp 36965 2008-02-21 15:33:20Z thiessen $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Authors: Paul Thiessen
27 *
28 * File Description:
29 * Classes to display sequences and alignments with wxWindows front end
30 *
31 * ===========================================================================
32 */
33 
34 #include <ncbi_pch.hpp>
35 #include <corelib/ncbistd.hpp>
36 #include <corelib/ncbi_limits.hpp>
37 
39 
41 #include "viewable_alignment.hpp"
42 #include "cn3d_tools.hpp"
43 
45 
46 
47 /////////////////////////////////////////////////////////////////////////////////
48 ///////// These are definitions of the sequence and title drawing areas /////////
49 /////////////////////////////////////////////////////////////////////////////////
50 
51 class SequenceViewerWidget_TitleArea : public wxWindow
52 {
53 public:
54  // constructor (same as wxWindow)
56  wxWindow* parent,
57  wxWindowID id = -1,
58  const wxPoint& pos = wxDefaultPosition,
59  const wxSize& size = wxDefaultSize
60  );
61 
62  // destructor
64 
65  void ShowTitles(ViewableAlignment *newAlignment);
66  void SetCharacterFont(wxFont *font, unsigned int newCellHeight);
67  void SetBackgroundColor(const wxColor& backgroundColor);
68 
69 private:
70  void OnPaint(wxPaintEvent& event);
71  void OnMouseEvent(wxMouseEvent& event);
72 
74 
77 
79  wxFont *titleFont;
81 
82  DECLARE_EVENT_TABLE()
83 
84 public:
85  unsigned int GetMaxTitleWidth(void) const { return maxTitleWidth; }
87  { sequenceArea = seqA; }
88 
89 };
90 
91 class SequenceViewerWidget_SequenceArea : public wxScrolledWindow
92 {
93  friend class SequenceViewerWidget;
94 
95 public:
96  // constructor (same as wxScrolledWindow)
98  wxWindow* parent,
99  wxWindowID id = -1,
100  const wxPoint& pos = wxDefaultPosition,
101  const wxSize& size = wxDefaultSize,
102  long style = wxHSCROLL | wxVSCROLL,
103  const wxString& name = "scrolledWindow"
104  );
105 
106  // destructor
108 
109  // stuff from parent widget
110  bool AttachAlignment(ViewableAlignment *newAlignment, int initX, int initY);
112  void SetBackgroundColor(const wxColor& backgroundColor);
113  void SetCharacterFont(wxFont *font);
114  void SetRubberbandColor(const wxColor& rubberbandColor);
115 
116  ///// everything else below here is part of the actual implementation /////
117 private:
118 
119  void OnPaint(wxPaintEvent& event);
120  void OnMouseEvent(wxMouseEvent& event);
121  void OnScrollWin(wxScrollWinEvent& event);
122 
125 
126  wxBitmap *bitmap; // for memoryDC/Blit
127 
128  wxFont *currentFont; // character font
129  wxColor currentBackgroundColor; // background for whole window
130  wxColor currentRubberbandColor; // color of rubber band
132  unsigned int cellWidth, cellHeight; // dimensions of cells in pixels
133  unsigned int areaWidth, areaHeight; // dimensions of the alignment (virtual area) in cells
134 
135  void DrawCell(wxDC& dc, unsigned int x, unsigned int y, unsigned int vsX, unsigned int vsY, bool redrawBackground); // draw a single cell
136 
139  eDot
140  };
142  void DrawLine(wxDC& dc, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2);
143  void DrawRubberband(wxDC& dc, // draw rubber band around cells
144  unsigned int fromX, unsigned int fromY, unsigned int toX, unsigned int toY, unsigned int vsX, unsigned int vsY);
145  void MoveRubberband(wxDC &dc, unsigned int fromX, unsigned int fromY, // change rubber band
146  unsigned int prevToX, unsigned int prevToY, unsigned int toX, unsigned int toY, unsigned int vsX, unsigned int vsY);
147  void RemoveRubberband(wxDC& dc, unsigned int fromX, unsigned int fromY, // remove rubber band
148  unsigned int toX, unsigned int toY, unsigned int vsX, unsigned int vsY);
149 
150  DECLARE_EVENT_TABLE()
151 
152 public:
153  unsigned int GetCellHeight(void) const { return cellHeight; }
154  unsigned int GetAreaHeight(void) const { return areaHeight; }
156  { titleArea = titleA; }
158 };
159 
160 
161 ///////////////////////////////////////////////////////////////////////////
162 ///////// This is the implementation of the sequence drawing area /////////
163 ///////////////////////////////////////////////////////////////////////////
164 
165 BEGIN_EVENT_TABLE(SequenceViewerWidget_SequenceArea, wxScrolledWindow)
170 
172  wxWindow* parent,
173  wxWindowID id,
174  const wxPoint& pos,
175  const wxSize& size,
176  long style,
177  const wxString& name
178  ) :
179  wxScrolledWindow(parent, -1, pos, size, style, name),
180  titleArea(NULL), alignment(NULL), bitmap(NULL), currentFont(NULL)
181 {
182  // set default background color
183  currentBackgroundColor = *wxWHITE;
184 
185  // set default mouse mode
187 
188  // set default rubber band color
189  currentRubberbandColor = *wxRED;
190 }
191 
193 {
194  if (currentFont) delete currentFont;
195  if (bitmap) delete bitmap;
196 }
197 
199  ViewableAlignment *newAlignment, int initX, int initY)
200 {
201  alignment = newAlignment;
202 
203  if (alignment) {
204  // set size of virtual area
206  if (areaWidth <= 0 || areaHeight <= 0) return false;
207 // TRACEMSG(Info << "area height " << areaHeight);
208 
209  // "+1" to make sure last real column and row are always visible, even
210  // if visible area isn't exact multiple of cell size
211  SetScrollbars(
213  areaWidth + 1, areaHeight + 1,
214  initX, initY,
215  true);
216 
217  alignment->MouseOver(-1, -1);
218 
219  } else {
220  // remove scrollbars
221 // areaWidth = areaHeight = 2;
222 // SetScrollbars(10, 10, 2, 2, 0, 0); //can't do this without crash on win98... ?
223  }
224 
225  return true;
226 }
227 
228 void SequenceViewerWidget_SequenceArea::SetBackgroundColor(const wxColor& backgroundColor)
229 {
230  currentBackgroundColor = backgroundColor;
231 }
232 
234 {
235  if (!font) return;
236 
237 // On Macs (PPC, Intel and PPC Mac binaries run on an Intel Mac):
238 // 'W' and 'M' are too big for the space available and are not drawn.
239 // Could use "W" in call to GetTextExtent but the alignment looks too spread out;
240 // visually it looks better by adding the extra pixel.
241 #if defined(__WXMAC__)
242  static const int cellWidthSpacer = 2;
243 #else
244  static const int cellWidthSpacer = 1;
245 #endif
246 
247  wxClientDC dc(this);
248  dc.SetFont(wxNullFont);
249 
250  if (currentFont) delete currentFont;
251  currentFont = font;
252 
253  dc.SetFont(*currentFont);
254  wxCoord chW, chH;
255  dc.SetMapMode(wxMM_TEXT);
256  dc.GetTextExtent("A", &chW, &chH);
257  cellWidth = chW + cellWidthSpacer;
258  cellHeight = chH;
259 
260  // need to reset scrollbars and virtual area size
261  AttachAlignment(alignment, 0, 0);
262 }
263 
265 {
266  mouseMode = mode;
267 }
268 
269 void SequenceViewerWidget_SequenceArea::SetRubberbandColor(const wxColor& rubberbandColor)
270 {
271  currentRubberbandColor = rubberbandColor;
272 }
273 
275 {
276  // adjust bitmap size to match client area size
277  if (!bitmap || bitmap->GetWidth() != GetClientSize().GetWidth() ||
278  bitmap->GetHeight() != GetClientSize().GetHeight()) {
279  if (bitmap) delete bitmap;
280  bitmap = new wxBitmap(GetClientSize().GetWidth(), GetClientSize().GetHeight());
281  }
282 
283  wxMemoryDC memDC;
284  memDC.SelectObject(*bitmap);
285 
286  int vsX, vsY,
287  updLeft, updRight, updTop, updBottom,
288  firstCellX, firstCellY,
289  lastCellX, lastCellY,
290  x, y;
291  static int prevVsY = -1;
292 
293 // memDC.BeginDrawing();
294 
295  // set font for characters
296  if (alignment) {
297  memDC.SetFont(*currentFont);
298  memDC.SetMapMode(wxMM_TEXT);
299  // characters to be drawn transparently over background
300  memDC.SetBackgroundMode(wxTRANSPARENT);
301 
302  // get upper left corner of visible area
303  GetViewStart(&vsX, &vsY); // returns coordinates in scroll units (cells)
304 // TRACEMSG("vsX=" << vsX << " vsY=" << vsY);
305  if (vsY != prevVsY) {
306  if (titleArea) titleArea->Refresh();
307  prevVsY = vsY;
308  }
309  }
310 
311  // get the update rect list, so that we can draw *only* the cells
312  // in the part of the window that needs redrawing; update region
313  // coordinates are relative to the visible part of the drawing area
314  wxRegionIterator upd(GetUpdateRegion());
315 
316  for (; upd; ++upd) {
317 // TRACEMSG("upd: x=" << upd.GetX() << " y=" << upd.GetY() <<
318 // " w=" << upd.GetW() << " h=" << upd.GetH());
319 
320  // draw background
321  memDC.SetPen(*(wxThePenList->
322  FindOrCreatePen(currentBackgroundColor, 1, wxSOLID)));
323  memDC.SetBrush(*(wxTheBrushList->
324  FindOrCreateBrush(currentBackgroundColor, wxSOLID)));
325  memDC.DrawRectangle(upd.GetX(), upd.GetY(), upd.GetW(), upd.GetH());
326 
327  if (!alignment) continue;
328 
329  // figure out which cells contain the corners of the update region
330 
331  // get coordinates of update region corners relative to virtual area
332  updLeft = vsX*cellWidth + upd.GetX();
333  updTop = vsY*cellHeight + upd.GetY();
334  updRight = updLeft + upd.GetW() - 1;
335  updBottom = updTop + upd.GetH() - 1;
336 
337  // firstCell[X,Y] is upper leftmost cell to draw, and is the cell
338  // that contains the upper left corner of the update region
339  firstCellX = updLeft / cellWidth;
340  firstCellY = updTop / cellHeight;
341 
342  // lastCell[X,Y] is the lower rightmost cell displayed; including partial
343  // cells if the visible area isn't an exact multiple of cell size. (It
344  // turns out to be very difficult to only display complete cells...)
345  lastCellX = updRight / cellWidth;
346  lastCellY = updBottom / cellHeight;
347 
348  // restrict to size of virtual area, if visible area is larger
349  // than the virtual area
350  if (lastCellX >= (int)areaWidth) lastCellX = areaWidth - 1;
351  if (lastCellY >= (int)areaHeight) lastCellY = areaHeight - 1;
352 
353  // draw cells
354 // TRACEMSG("drawing cells " << firstCellX << ',' << firstCellY << " to " << lastCellX << ',' << lastCellY
355 // << "; vsX,vsY = " << vsX << ',' << vsY);
356  for (y=firstCellY; y<=lastCellY; ++y) {
357  for (x=firstCellX; x<=lastCellX; ++x) {
358  DrawCell(memDC, x, y, vsX, vsY, false);
359  }
360  }
361  }
362 
363 // memDC.EndDrawing();
364 
365  // Blit from memory DC to paintDC to avoid flicker
366  wxPaintDC paintDC(this);
367  paintDC.Blit(0, 0, GetClientSize().GetWidth(), GetClientSize().GetHeight(), &memDC, 0,0, wxCOPY);
368 // TRACEMSG("Blit 0, 0, " << GetClientSize().GetWidth() << ", " << GetClientSize().GetHeight());
369 }
370 
372  unsigned int x, unsigned int y, unsigned int vsX, unsigned int vsY, bool redrawBackground)
373 {
374  char character;
375  wxColor color, cellBackgroundColor;
376  bool drawBackground, drawChar;
377 
378  drawChar = alignment->GetCharacterTraitsAt(x, y, &character, &color, &drawBackground, &cellBackgroundColor);
379 
380  // adjust x,y into visible area coordinates
381  x = (x - vsX) * cellWidth;
382  y = (y - vsY) * cellHeight;
383 
384  // if necessary, redraw background with appropriate color
385  if ((drawChar && drawBackground) || redrawBackground) {
386  if (drawChar && drawBackground) {
387  dc.SetPen(*(wxThePenList->FindOrCreatePen(cellBackgroundColor, 1, wxSOLID)));
388  dc.SetBrush(*(wxTheBrushList->FindOrCreateBrush(cellBackgroundColor, wxSOLID)));
389  } else {
390  dc.SetPen(*(wxThePenList->FindOrCreatePen(currentBackgroundColor, 1, wxSOLID)));
391  dc.SetBrush(*(wxTheBrushList->FindOrCreateBrush(currentBackgroundColor, wxSOLID)));
392  }
393  dc.DrawRectangle(x, y, cellWidth, cellHeight);
394  }
395 
396  if (!drawChar) return;
397 
398  // set character color
399  dc.SetTextForeground(color);
400 
401  // measure character size
402  wxString buf(character);
403  wxCoord chW, chH;
404  dc.GetTextExtent(buf, &chW, &chH);
405 
406  // draw character in the middle of the cell
407  dc.DrawText(buf,
408  x + (cellWidth - chW)/2,
409  y + (cellHeight - chH)/2
410  );
411 }
412 
413 template < class I >
414 void min_max(I a,I b, I *c, I *d)
415 {
416  if (a <= b) {
417  *c = a;
418  *d = b;
419  } else {
420  *c = b;
421  *d = a;
422  }
423 }
424 
425 void SequenceViewerWidget_SequenceArea::DrawLine(wxDC& dc, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2)
426 {
427  if (currentRubberbandType == eSolid) {
428  dc.DrawLine(x1, y1, x2, y2);
429  } else { // short-dashed line
430  unsigned int i, ie;
431  if (x1 == x2) { // vertical line
432  min_max(y1, y2, &i, &ie);
433  --ie;
434  for (; i<=ie; ++i)
435  if (i%4 == 0) dc.DrawLine(x1, i, x1, i + 2);
436  } else { // horizontal line
437  min_max(x1, x2, &i, &ie);
438  --ie;
439  for (; i<=ie; ++i)
440  if (i%4 == 0) dc.DrawLine(i, y1, i + 2, y1);
441  }
442  }
443 }
444 
445 // draw a rubberband around the cells
446 void SequenceViewerWidget_SequenceArea::DrawRubberband(wxDC& dc, unsigned int fromX, unsigned int fromY,
447  unsigned int toX, unsigned int toY, unsigned int vsX, unsigned int vsY)
448 {
449  // find upper-left and lower-right corners
450  int minX, minY, maxX, maxY;
451  min_max((int)fromX, (int)toX, &minX, &maxX);
452  min_max((int)fromY, (int)toY, &minY, &maxY);
453 
454  // convert to pixel coordinates of corners
455  minX = (minX - vsX) * cellWidth;
456  minY = (minY - vsY) * cellHeight;
457  maxX = (maxX - vsX) * cellWidth + cellWidth - 1;
458  maxY = (maxY - vsY) * cellHeight + cellHeight - 1;
459  if (maxX < minX || maxY < minY)
460  return;
461 
462  // set color
463  dc.SetPen(*(wxThePenList->FindOrCreatePen(currentRubberbandColor, 1, wxSOLID)));
464 
465  // draw sides (should draw in order, due to pixel roundoff)
467  DrawLine(dc, minX, minY, maxX, minY); // top
469  DrawLine(dc, maxX, minY, maxX, maxY); // right
471  DrawLine(dc, maxX, maxY, minX, maxY); // bottom
473  DrawLine(dc, minX, maxY, minX, minY); // left
474 }
475 
476 // move the rubber band to a new rectangle, erasing only the side(s) of the
477 // rectangle that is changing
478 void SequenceViewerWidget_SequenceArea::MoveRubberband(wxDC &dc, unsigned int fromX, unsigned int fromY,
479  unsigned int prevToX, unsigned int prevToY, unsigned int toX, unsigned int toY, unsigned int vsX, unsigned int vsY)
480 {
481  unsigned int i;
482 
483  if ((prevToX >= fromX && toX < fromX) ||
484  (prevToX < fromX && toX >= fromX) ||
485  (prevToY >= fromY && toY < fromY) ||
486  (prevToY < fromY && toY >= fromY)) {
487  // need to completely redraw if rectangle is "flipped"
488  RemoveRubberband(dc, fromX, fromY, prevToX, prevToY, vsX, vsY);
489 
490  } else {
491  unsigned int a, b;
492 
493  // erase moving bottom/top side if dragging up/down
494  if (toY != prevToY) {
495  min_max(fromX, prevToX, &a, &b);
496  for (i=a; i<=b; ++i) DrawCell(dc, i, prevToY, vsX, vsY, true);
497  }
498 
499  // erase partial top and bottom if dragging left by more than one
500  a = kMax_UInt; b = a - 1;
501  if (fromX <= toX && toX < prevToX) {
502  a = toX + 1;
503  b = prevToX - 1;
504  } else if (prevToX < toX && toX < fromX) {
505  a = prevToX + 1;
506  b = toX - 1;
507  }
508  for (i=a; i<=b; ++i) {
509  DrawCell(dc, i, fromY, vsX, vsY, true);
510  DrawCell(dc, i, prevToY, vsX, vsY, true);
511  }
512 
513  // erase moving left/right side
514  if (toX != prevToX) {
515  min_max(fromY, prevToY, &a, &b);
516  for (i=a; i<=b; ++i) DrawCell(dc, prevToX, i, vsX, vsY, true);
517  }
518 
519  // erase partial left and right sides if dragging up/down by more than one
520  a = kMax_UInt; b = a - 1;
521  if (fromY <= toY && toY < prevToY) {
522  a = toY + 1;
523  b = prevToY - 1;
524  } else if (prevToY < toY && toY < fromY) {
525  a = prevToY + 1;
526  b = toY - 1;
527  }
528  for (i=a; i<=b; ++i) {
529  DrawCell(dc, fromX, i, vsX, vsY, true);
530  DrawCell(dc, prevToX, i, vsX, vsY, true);
531  }
532  }
533 
534  // redraw whole new one
535  DrawRubberband(dc, fromX, fromY, toX, toY, vsX, vsY);
536 }
537 
538 // redraw only those cells necessary to remove rubber band
539 void SequenceViewerWidget_SequenceArea::RemoveRubberband(wxDC& dc, unsigned int fromX, unsigned int fromY,
540  unsigned int toX, unsigned int toY, unsigned int vsX, unsigned int vsY)
541 {
542  unsigned int i, min, max;
543 
544  // remove top and bottom
545  min_max(fromX, toX, &min, &max);
546  for (i=min; i<=max; ++i) {
547  DrawCell(dc, i, fromY, vsX, vsY, true);
548  DrawCell(dc, i, toY, vsX, vsY, true);
549  }
550  // remove left and right
551  min_max(fromY, toY, &min, &max);
552  if (max - min > 1) {
553  for (i=min+1; i<=max-1; ++i) {
554  DrawCell(dc, fromX, i, vsX, vsY, true);
555  DrawCell(dc, toX, i, vsX, vsY, true);
556  }
557  }
558 }
559 
561 {
562  // when scrolling happens via scrollbars (or movement keys), need to update status bar info.
563  // So, fake a (non-moving) mouse wheel event, which will trigger MouseOver.
564  wxMouseEvent fake(wxEVT_MOUSEWHEEL);
565  fake.m_wheelRotation = 0;
566  fake.m_wheelDelta = 120;
567  fake.m_linesPerAction = 3;
568  AddPendingEvent(fake);
569  event.Skip(); // continue to process this event normally
570 }
571 
573 {
574  static const ViewableAlignment *prevAlignment = NULL;
575  static int prevMOX = -1, prevMOY = -1;
576  static bool dragging = false;
577 
578  if (!alignment) {
579  prevAlignment = NULL;
580  prevMOX = prevMOY = -1;
581  dragging = false;
582  return;
583  }
584  if (alignment != prevAlignment) {
585  prevMOX = prevMOY = -1;
586  prevAlignment = alignment;
587  dragging = false;
588  }
589 
590  // get coordinates of mouse when it's over the drawing area
591  wxCoord mX, mY;
592  event.GetPosition(&mX, &mY);
593 
594  // get current view window location
595  int vsX, vsY;
596  GetViewStart(&vsX, &vsY);
597 
598  // handle wheel events
599  static wxCoord windowMX = 0, windowMY = 0;
600  bool wheelEvent = (event.GetEventType() == wxEVT_MOUSEWHEEL);
601  if (wheelEvent) {
602  if (dragging || windowMX < 0 || windowMY < 0 ||
603  windowMX >= GetClientSize().GetWidth() || windowMY >= GetClientSize().GetHeight())
604  return;
605  mX = windowMX; // coords on mouse wheel event seem to be screen-relative, not window-relative
606  mY = windowMY;
607  static int accumulatedRotation = 0;
608  accumulatedRotation -= event.GetWheelRotation(); // move wheel up -> scroll up
609  int nDeltas = accumulatedRotation / event.GetWheelDelta();
610  if (nDeltas != 0) {
611  accumulatedRotation -= nDeltas * event.GetWheelDelta();
612  int toY = vsY + nDeltas * event.GetLinesPerAction();
613  if (toY < 0)
614  toY = 0;
615  else if (toY >= (int)areaHeight)
616  toY = areaHeight - 1;
617  Scroll(-1, toY);
618  GetViewStart(&vsX, &vsY); // update vsY so that MouseOver is called on new position
619  }
620  } else {
621  windowMX = mX;
622  windowMY = mY;
623  }
624 
625  // translate visible area coordinates to cell coordinates
626  int cellX, cellY, MOX, MOY;
627  cellX = MOX = vsX + mX / cellWidth;
628  cellY = MOY = vsY + mY / cellHeight;
629 
630  // if the mouse is leaving the window, use cell coordinates of most
631  // recent known mouse-over cell
632  if (event.Leaving()) {
633  cellX = prevMOX;
634  cellY = prevMOY;
635  MOX = MOY = -1;
636  }
637 
638  // do MouseOver if not in the same cell (or outside area) as last time
639  if (MOX >= (int)areaWidth || MOY >= (int)areaHeight)
640  MOX = MOY = -1;
641  if (MOX != prevMOX || MOY != prevMOY)
642  alignment->MouseOver(MOX, MOY);
643  prevMOX = MOX;
644  prevMOY = MOY;
645 
646  if (wheelEvent)
647  return;
648 
649  // adjust for column/row selection
651  cellY = vsY + GetClientSize().GetHeight() / cellHeight;
653  cellX = vsX + GetClientSize().GetWidth() / cellWidth;
654 
655  // limit coordinates of selection to virtual area
656  if (cellX < 0) cellX = 0;
657  else if (cellX >= (int)areaWidth) cellX = areaWidth - 1;
658  if (cellY < 0) cellY = 0;
659  else if (cellY >= (int)areaHeight) cellY = areaHeight - 1;
660 
661 
662  // keep track of position of selection start, as well as last
663  // cell dragged to during selection
664  static unsigned int fromX, fromY, prevToX, prevToY;
665 
666  // limit dragging movement if necessary
667  if (dragging) {
668  if (mouseMode == SequenceViewerWidget::eDragHorizontal) cellY = fromY;
669  if (mouseMode == SequenceViewerWidget::eDragVertical) cellX = fromX;
670  }
671 
672  // process beginning of selection
673  if (event.LeftDown()) {
674 
675  // find out which (if any) control keys are down at this time
676  unsigned int controls = 0;
677  if (event.ShiftDown()) controls |= ViewableAlignment::eShiftDown;
678 #ifdef __WXMAC__
679  if (event.MetaDown()) // control key + mouse doesn't work on Mac?
680 #else
681  if (event.ControlDown())
682 #endif
684  if (event.AltDown() || event.MetaDown()) controls |= ViewableAlignment::eAltOrMetaDown;
685 
686  // send MouseDown message; don't start selection if MouseDown returns false
687  // or if not inside display area
688  if (alignment->MouseDown(MOX, MOY, controls) && MOX != -1) {
689  prevToX = fromX = cellX;
690  prevToY = fromY = cellY;
691  dragging = true;
692 
693  TRACEMSG("drawing initial rubberband");
694  wxClientDC dc(this);
695 // dc.BeginDrawing();
701 
703  fromY = vsY;
704  prevToY = cellY;
705  DrawRubberband(dc, fromX, fromY, fromX, cellY, vsX, vsY);
707  fromX = vsX;
708  prevToX = cellX;
709  DrawRubberband(dc, fromX, fromY, cellX, fromY, vsX, vsY);
710  } else {
711  DrawRubberband(dc, fromX, fromY, fromX, fromY, vsX, vsY);
712  }
713 // dc.EndDrawing();
714  }
715  }
716 
717  // process end of selection, on mouse-up, or if the mouse leaves the window
718  else if (dragging && (event.LeftUp() || event.Leaving() || event.Entering())) {
719  if (!event.LeftUp()) {
720  cellX = prevToX;
721  cellY = prevToY;
722  }
723 
724  dragging = false;
725  wxClientDC dc(this);
726 // dc.BeginDrawing();
727  dc.SetFont(*currentFont);
728 
729  // remove rubberband
734  {
735  RemoveRubberband(dc, fromX, fromY, cellX, cellY, vsX, vsY);
736  } else {
737  DrawCell(dc, fromX, fromY, vsX, vsY, true);
738  if (cellX != (int)fromX || cellY != (int)fromY)
739  DrawCell(dc, cellX, cellY, vsX, vsY, true);
740  }
741 // dc.EndDrawing();
742 
743  // adjust for column/row selection
745  fromY = 0;
746  cellY = areaHeight - 1;
748  fromX = 0;
749  cellX = areaWidth - 1;
750  }
751 
752  // do appropriate callback
757  {
759  ((int)fromX < cellX) ? fromX : cellX,
760  ((int)fromY < cellY) ? fromY : cellY,
761  (cellX > (int)fromX) ? cellX : fromX,
762  (cellY > (int)fromY) ? cellY : fromY);
763  } else {
764  alignment->DraggedCell(fromX, fromY, cellX, cellY);
765  }
766  }
767 
768  // process continuation of selection - redraw rectangle
769  else if (dragging && (cellX != (int)prevToX || cellY != (int)prevToY)) {
770 
771  wxClientDC dc(this);
772 // dc.BeginDrawing();
773  dc.SetFont(*currentFont);
779  {
780  MoveRubberband(dc, fromX, fromY, prevToX, prevToY, cellX, cellY, vsX, vsY);
781  } else {
782  if (prevToX != fromX || prevToY != fromY)
783  DrawCell(dc, prevToX, prevToY, vsX, vsY, true);
784  if (cellX != (int)fromX || cellY != (int)fromY) {
785  DrawRubberband(dc, cellX, cellY, cellX, cellY, vsX, vsY);
786  }
787  }
788 // dc.EndDrawing();
789  prevToX = cellX;
790  prevToY = cellY;
791  }
792 }
793 
794 
795 ////////////////////////////////////////////////////////////////////////
796 ///////// This is the implementation of the title drawing area /////////
797 ////////////////////////////////////////////////////////////////////////
798 
799 BEGIN_EVENT_TABLE(SequenceViewerWidget_TitleArea, wxWindow)
803 
805  wxWindow* parent,
806  wxWindowID id,
807  const wxPoint& pos,
808  const wxSize& size) :
809  wxWindow(parent, id, pos, size, wxNO_BORDER),
810  sequenceArea(NULL), alignment(NULL), highlightedTitleRow(-1), titleFont(NULL),
811  cellHeight(0), maxTitleWidth(20)
812 {
813  currentBackgroundColor = *wxWHITE;
814 }
815 
817 {
818  if (titleFont) delete titleFont;
819 }
820 
822 {
823  alignment = newAlignment;
824  highlightedTitleRow = -1;
825  if (!alignment) return;
826 
827  // set font
828  wxClientDC dc(this);
829  dc.SetFont(*titleFont);
830  dc.SetMapMode(wxMM_TEXT);
831 
832  unsigned int i;
833  wxString title;
834  wxColor color;
835  wxCoord tW, tH;
836 
837  // get maximum width of any title
838  alignment->GetSize(&i, &nTitles);
839  if (nTitles <= 0) return;
840  maxTitleWidth = 20;
841  for (i=0; i<nTitles; ++i) {
842  if (!alignment->GetRowTitle(i, &title, &color)) continue;
843  // measure title size
844  dc.GetTextExtent(title, &tW, &tH);
845  if (tW > (wxCoord)maxTitleWidth) maxTitleWidth = tW;
846  }
847 }
848 
849 void SequenceViewerWidget_TitleArea::SetCharacterFont(wxFont *font, unsigned int newCellHeight)
850 {
851  if (!font) return;
852 
853  if (titleFont) delete titleFont;
854  titleFont = font;
855  cellHeight = newCellHeight;
856 
858 }
859 
860 void SequenceViewerWidget_TitleArea::SetBackgroundColor(const wxColor& backgroundColor)
861 {
862  currentBackgroundColor = backgroundColor;
863 }
864 
866 {
867  wxPaintDC dc(this);
868 
869  int vsX, vsY, updTop, updBottom, firstRow, lastRow, row;
870 
871 // dc.BeginDrawing();
872 
873  // set font for characters
874  if (alignment) {
875  dc.SetFont(*titleFont);
876  dc.SetMapMode(wxMM_TEXT);
877  // characters to be drawn transparently over background
878  dc.SetBackgroundMode(wxTRANSPARENT);
879 
880  // get upper left corner of visible area
881  sequenceArea->GetViewStart(&vsX, &vsY); // returns coordinates in scroll units (cells)
882  }
883 
884  // get the update rect list, so that we can draw *only* the cells
885  // in the part of the window that needs redrawing; update region
886  // coordinates are relative to the visible part of the drawing area
887  wxRegionIterator upd(GetUpdateRegion());
888 
889  for (; upd; ++upd) {
890 
891 // if (upd.GetW() == GetClientSize().GetWidth() &&
892 // upd.GetH() == GetClientSize().GetHeight())
893 // TESTMSG("repainting whole SequenceViewerWidget_TitleArea");
894 
895  // draw background
896  dc.SetPen(*(wxThePenList->
897  FindOrCreatePen(currentBackgroundColor, 1, wxSOLID)));
898  dc.SetBrush(*(wxTheBrushList->
899  FindOrCreateBrush(currentBackgroundColor, wxSOLID)));
900  dc.DrawRectangle(upd.GetX(), upd.GetY(), upd.GetW(), upd.GetH());
901 
902  if (!alignment) continue;
903 
904  // figure out which cells contain the corners of the update region
905 
906  // get coordinates of update region corners relative to virtual area
907  updTop = vsY*cellHeight + upd.GetY();
908  updBottom = updTop + upd.GetH() - 1;
909  firstRow = updTop / cellHeight;
910  lastRow = updBottom / cellHeight;
911 
912  // restrict to size of virtual area, if visible area is larger
913  // than the virtual area
914  if (lastRow >= (int)nTitles) lastRow = nTitles - 1;
915 
916  // draw titles
917  wxString title;
918  wxColor color;
919  wxCoord tW, tH;
920  for (row=firstRow; row<=lastRow; ++row) {
921 
922  if (!alignment->GetRowTitle(row, &title, &color)) continue;
923 
924  // set character color
925  if (row == highlightedTitleRow)
926  dc.SetTextForeground(*wxRED);
927  else
928  dc.SetTextForeground(color);
929 
930  // draw title centered vertically in the cell
931  dc.GetTextExtent(title, &tW, &tH);
932  dc.DrawText(title, 0, cellHeight * (row - vsY) + (cellHeight - tH)/2);
933  }
934  }
935 
936 // dc.EndDrawing();
937 }
938 
940 {
941  static const ViewableAlignment *prevAlignment = NULL;
942  static int prevMOY = -1;
943 
944  if (!alignment) {
945  prevAlignment = NULL;
946  prevMOY = -1;
947  return;
948  }
949  if (alignment != prevAlignment) {
950  prevMOY = -1;
951  prevAlignment = alignment;
952  }
953 
954  // get coordinates of mouse when it's over the drawing area
955  wxCoord mX, mY;
956  event.GetPosition(&mX, &mY);
957 
958  // translate visible area coordinates to cell coordinates
959  int vsX, vsY, MOY;
960  sequenceArea->GetViewStart(&vsX, &vsY);
961  MOY = vsY + mY / cellHeight;
962 
963  // if the mouse is leaving the window or is outside area
964  if (event.Leaving() || MOY >= (int)sequenceArea->GetAreaHeight())
965  MOY = -1;
966 
967  // do MouseOver if not in the same cell as last time
968  if (MOY != prevMOY)
969  alignment->MouseOver(-1, MOY);
970  prevMOY = MOY;
971 
972  // process button press
973  if (event.LeftDown()) {
974  // find out which (if any) control keys are down at this time
975  unsigned int controls = 0;
976  if (event.ShiftDown()) controls |= ViewableAlignment::eShiftDown;
977 #ifdef __WXMAC__
978  if (event.MetaDown()) // control key + mouse doesn't work on Mac?
979 #else
980  if (event.ControlDown())
981 #endif
983  if (event.AltDown() || event.MetaDown()) controls |= ViewableAlignment::eAltOrMetaDown;
984 
985  // send MouseDown message
986  alignment->MouseDown(-1, MOY, controls);
987 
988  // (temporarily) highlight this title
989  highlightedTitleRow = MOY;
990  Refresh();
991  }
992 }
993 
994 //////////////////////////////////////////////////////////////////////
995 ///////// This is the implementation of SequenceViewerWidget /////////
996 //////////////////////////////////////////////////////////////////////
997 
998 BEGIN_EVENT_TABLE(SequenceViewerWidget, wxSplitterWindow)
999  EVT_SPLITTER_DCLICK(-1, SequenceViewerWidget::OnDoubleClick)
1001 
1003  wxWindow* parent,
1004  wxWindowID id,
1005  const wxPoint& pos,
1006  const wxSize& size) :
1007  wxSplitterWindow(parent, -1, wxPoint(0,0), parent->GetClientSize(), wxSP_BORDER)
1008 {
1011 
1014 
1015  SplitVertically(titleArea, sequenceArea);
1016  SetMinimumPaneSize(50);
1017 }
1018 
1020 {
1021 }
1022 
1023 void SequenceViewerWidget::OnDoubleClick(wxSplitterEvent& event)
1024 {
1025  Unsplit(titleArea);
1026 }
1027 
1029 {
1030  if (!IsSplit()) {
1031  titleArea->Show(true);
1032  SplitVertically(titleArea, sequenceArea, titleArea->GetMaxTitleWidth() + 10);
1033  }
1034 }
1035 
1037 {
1038  if (IsSplit())
1039  Unsplit(titleArea);
1040 }
1041 
1043 {
1044  if (IsSplit())
1045  Unsplit(titleArea);
1046  else {
1047  titleArea->Show(true);
1048  SplitVertically(titleArea, sequenceArea, titleArea->GetMaxTitleWidth() + 10);
1049  }
1050 }
1051 
1052 bool SequenceViewerWidget::AttachAlignment(ViewableAlignment *newAlignment, int initX, int initY)
1053 {
1054  // do titles first, since on Mac sequence area update causes title redraw
1055  titleArea->ShowTitles(newAlignment);
1056  sequenceArea->AttachAlignment(newAlignment, initX, initY);
1057 
1058  SetSashPosition(titleArea->GetMaxTitleWidth() + 10, true);
1059  return true;
1060 }
1061 
1063 {
1065 }
1066 
1068 {
1069  return sequenceArea->GetMouseMode();
1070 }
1071 
1072 void SequenceViewerWidget::SetBackgroundColor(const wxColor& backgroundColor)
1073 {
1074  sequenceArea->SetBackgroundColor(backgroundColor);
1075  titleArea->SetBackgroundColor(backgroundColor);
1076 }
1077 
1079 {
1080  wxFont *newFont = new wxFont(font->GetPointSize(), font->GetFamily(),
1081  wxNORMAL, wxNORMAL, false, font->GetFaceName(), font->GetDefaultEncoding());
1082  sequenceArea->SetCharacterFont(newFont);
1083  newFont = new wxFont(font->GetPointSize(), font->GetFamily(),
1084  wxITALIC, wxNORMAL, false, font->GetFaceName(), font->GetDefaultEncoding());
1086  delete font;
1087 
1088  // toggle title area twice to reset the sash to the right width - crude but effective
1089  TitleAreaToggle();
1090  TitleAreaToggle();
1091  Refresh(true);
1092 }
1093 
1094 void SequenceViewerWidget::SetRubberbandColor(const wxColor& rubberbandColor)
1095 {
1096  sequenceArea->SetRubberbandColor(rubberbandColor);
1097 }
1098 
1100 {
1101  sequenceArea->Scroll(column, row);
1102 }
1103 
1104 void SequenceViewerWidget::GetScroll(int *vsX, int *vsY) const
1105 {
1106  sequenceArea->GetViewStart(vsX, vsY);
1107 }
1108 
1110 {
1111  int vsX, vsY;
1112  unsigned int nCells;
1113  sequenceArea->GetViewStart(&vsX, &vsY);
1114 
1115  // scroll horizontally if necessary
1116  if (column >= 0) {
1117  nCells = sequenceArea->GetClientSize().GetWidth() / sequenceArea->cellWidth;
1118  if (column < vsX || column >= vsX + (int)nCells) {
1119  vsX = column - nCells / 2;
1120  if (vsX < 0) vsX = 0;
1121  }
1122  }
1123 
1124  // scroll vertically if necessary
1125  if (row >= 0) {
1126  nCells = sequenceArea->GetClientSize().GetHeight() / sequenceArea->cellHeight;
1127  if (row < vsY || row >= vsY + (int)nCells) {
1128  vsY = row - nCells / 2;
1129  if (vsY < 0) vsY = 0;
1130  }
1131  }
1132 
1133  sequenceArea->Scroll(vsX, vsY);
1134 }
1135 
1136 void SequenceViewerWidget::Refresh(bool eraseBackground, const wxRect *rect)
1137 {
1138  sequenceArea->Refresh(eraseBackground, rect);
1139  titleArea->Refresh(eraseBackground, rect);
1140 }
SequenceViewerWidget_SequenceArea(wxWindow *parent, wxWindowID id=-1, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxHSCROLL|wxVSCROLL, const wxString &name="scrolledWindow")
void MoveRubberband(wxDC &dc, unsigned int fromX, unsigned int fromY, unsigned int prevToX, unsigned int prevToY, unsigned int toX, unsigned int toY, unsigned int vsX, unsigned int vsY)
void DrawRubberband(wxDC &dc, unsigned int fromX, unsigned int fromY, unsigned int toX, unsigned int toY, unsigned int vsX, unsigned int vsY)
void OnScrollWin(wxScrollWinEvent &event)
bool AttachAlignment(ViewableAlignment *newAlignment, int initX, int initY)
void SetTitleArea(SequenceViewerWidget_TitleArea *titleA)
void SetBackgroundColor(const wxColor &backgroundColor)
void SetMouseMode(SequenceViewerWidget::eMouseMode mode)
SequenceViewerWidget_TitleArea * titleArea
void DrawLine(wxDC &dc, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2)
void RemoveRubberband(wxDC &dc, unsigned int fromX, unsigned int fromY, unsigned int toX, unsigned int toY, unsigned int vsX, unsigned int vsY)
SequenceViewerWidget::eMouseMode mouseMode
void SetRubberbandColor(const wxColor &rubberbandColor)
void DrawCell(wxDC &dc, unsigned int x, unsigned int y, unsigned int vsX, unsigned int vsY, bool redrawBackground)
SequenceViewerWidget::eMouseMode GetMouseMode(void) const
void ShowTitles(ViewableAlignment *newAlignment)
const SequenceViewerWidget_SequenceArea * sequenceArea
void SetCharacterFont(wxFont *font, unsigned int newCellHeight)
void OnMouseEvent(wxMouseEvent &event)
unsigned int GetMaxTitleWidth(void) const
void SetBackgroundColor(const wxColor &backgroundColor)
void SetSequenceArea(const SequenceViewerWidget_SequenceArea *seqA)
SequenceViewerWidget_TitleArea(wxWindow *parent, wxWindowID id=-1, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize)
void ScrollTo(int column, int row)
void OnDoubleClick(wxSplitterEvent &event)
void SetBackgroundColor(const wxColor &backgroundColor)
bool AttachAlignment(ViewableAlignment *newAlignment, int initX=0, int initY=0)
void GetScroll(int *vsX, int *vsY) const
void MakeCharacterVisible(int column, int row) const
SequenceViewerWidget_TitleArea * titleArea
void SetRubberbandColor(const wxColor &rubberbandColor)
eMouseMode GetMouseMode(void) const
void Refresh(bool eraseBackground=TRUE, const wxRect *rect=NULL)
SequenceViewerWidget_SequenceArea * sequenceArea
void SetMouseMode(eMouseMode mode)
void SetCharacterFont(wxFont *font)
virtual bool MouseDown(int column, int row, unsigned int controls)
virtual void MouseOver(int column, int row) const
virtual void SelectedRectangle(int columnLeft, int rowTop, int columnRight, int rowBottom)
virtual void DraggedCell(int columnFrom, int rowFrom, int columnTo, int rowTo)
virtual bool GetCharacterTraitsAt(unsigned int column, unsigned int row, char *character, wxColour *color, bool *drawBackground, wxColour *cellBackgroundColor) const =0
virtual void GetSize(unsigned int *columns, unsigned int *rows) const =0
virtual bool GetRowTitle(unsigned int row, wxString *title, wxColour *color) const =0
#define TRACEMSG(stream)
Definition: cn3d_tools.hpp:83
Include a standard set of the NCBI C++ Toolkit most basic headers.
#define NULL
Definition: ncbistd.hpp:225
#define kMax_UInt
Definition: ncbi_limits.h:185
n background color
END_EVENT_TABLE()
char * buf
int i
mdb_mode_t mode
Definition: lmdb++.h:38
const struct ncbi::grid::netcache::search::fields::SIZE size
unsigned int a
Definition: ncbi_localip.c:102
T max(T x_, T y_)
T min(T x_, T y_)
void min_max(I a, I b, I *c, I *d)
static const char * column
Definition: stats.c:23
#define const
Definition: zconf.h:230
Modified on Wed Nov 29 02:21:30 2023 by modify_doxy.py rev. 669887