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

Go to the SVN repository for this file.

1 /* $Id: alnmulti_header_handler.cpp 42648 2019-03-28 14:36:06Z katargir $
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: Andrey Yazhuk
27  *
28  * File Description:
29  *
30  */
31 
32 #include <ncbi_pch.hpp>
33 
35 
36 #include <gui/opengl/glhelpers.hpp>
37 #include <gui/opengl/irender.hpp>
38 
39 #include <wx/settings.h>
40 
42 
43 ////////////////////////////////////////////////////////////////////////////////
44 /// CAlnMultiHeaderHandler
45 
46 BEGIN_EVENT_TABLE(CAlnMultiHeaderHandler, wxEvtHandler)
52 
54 
55 // It probably is good idea to have a minumal column width
56 // to allow users adjust the width easier.
58 
60 : m_Host(NULL),
61  m_Pane(NULL),
62  m_State(eIdle),
63  m_HitResult(eNone),
64  m_DragIndex(-1), m_ResizableIndex(-1),
65  m_CurrInsIndex(-1)
66 {
67  m_DragArea = wxSystemSettings::GetMetric(wxSYS_DRAG_X);
68 }
69 
70 
72 {
73  m_Host = host;
74 }
75 
76 
78 {
79  return dynamic_cast<IGenericHandlerHost*>(m_Host);
80 }
81 
82 
84 {
85  return m_State != eIdle;
86 }
87 
88 
90 {
91  m_Pane = pane;
92 }
93 
94 
96 {
97  return this;
98 }
99 
100 
101 void CAlnMultiHeaderHandler::OnLeftDown(wxMouseEvent& event)
102 {
104 
105  wxPoint ms_pos = event.GetPosition();
107  m_State = ePushed;
108 
110 
112 }
113 
114 
115 void CAlnMultiHeaderHandler::OnMotion(wxMouseEvent& event)
116 {
117  _ASSERT(m_Host);
118  wxPoint ms_pos = event.GetPosition();
119 
120  if(event.Dragging()) {
123 
124  if(m_State == ePushed) {
126 
127  int d_x = m_VPMousePos.X() - m_VPPushPos.X();
128  int d_y = m_VPMousePos.Y() - m_VPPushPos.Y();
129  int dd = d_x * d_x + d_y * d_y;
130 
131  if(dd > m_DragArea * m_DragArea) { // dragged far enough
132  switch(m_HitResult) {
133  case eBorder: {
135  break;
136  }
137  case eColumn: {
139  break;
140  }
141  default: break;
142  }
143  }
144  } else {
145  switch(m_State) {
146  case eResize: {
147  int d_x = m_VPMousePos.X() - m_VPPushPos.X();
149  break;
150  };
151  case eMove:
152  x_MoveColumn();
153  break;
154  default: break;
155  };
156  }
157 
159  } else {
162  event.Skip();
163  }
165 }
166 
167 
168 void CAlnMultiHeaderHandler::OnLeftUp(wxMouseEvent& event)
169 {
170  if(m_State != eIdle) {
171  x_SwitchToIdleState(true);
172  } else {
173  event.Skip();
174  }
175 }
176 
177 
178 void CAlnMultiHeaderHandler::OnKeyDown(wxKeyEvent& event)
179 {
180  if(m_State != eIdle) {
181  int key = event.GetKeyCode();
182  if(key == WXK_ESCAPE) {
183  x_SwitchToIdleState(false);
184  }
185  } else {
186  event.Skip();
187  }
188 }
189 
190 
192  CAlnMultiHeaderHandler::x_HitTest(const TVPPoint& point, int& index)
193 {
195 
197  // get index of the column that physically contains the mouse pointer
198  index = context->GetColumnIndexByX(point.X());
199 
200  int col_n = context->GetColumnsCount();
201  if(index >= 0 && index < col_n) {
202  const TColumn& column = context->GetColumn(index);
203  int right = column.m_Pos + column.m_Width - 1;
204  int x = point.X();
205 
206  if(x >= right - kGrabZone && index < col_n - 1) { // right border
207  return eBorder;
208  } else if(x < column.m_Pos + kGrabZone) { // left border
209  for( index--; index >= 0; index--) {
210  const TColumn& col = context->GetColumn(index);
211  if(col.m_Visible) {
212  return eBorder;
213  }
214  }
215  return eNone;
216  } else {
217  return eColumn;
218  }
219  } else {
220  index = -1;
221  return eNone;
222  }
223 }
224 
225 
226 // initiates "resising by dragging" session
228 {
230 
232  int col_n = context->GetColumnsCount();
233 
234  if(m_DragIndex < col_n - 1) {
235  m_State = eResize;
236 
237  // initializing dragging state
238  m_Columns.clear();
239  m_Columns.resize(col_n);
240  for( int i = 0; i < col_n; i++ ) {
241  m_Columns[i] = context->GetColumn(i);
242  }
243 
244  //TODO clean-up later this dead old code
245  //int right_i = x_NextVisibleColumn(m_DragIndex, true);
246  //m_OrigPos = m_Columns[right_i].m_Pos;
247 
248  m_OrigPos = m_Columns[m_DragIndex].Right();
250 
251  // calculate limits for separator dragging
252  if(x_HasResizableColumn()) {
253  int resizable_width = m_Columns[m_ResizableIndex].m_Width;
254 
256  // we can move to the left untill the resized column width is >= 0
257  m_LeftLimit = (m_DragIndex == 0 ? 0 : m_Columns[m_DragIndex].m_Pos) + kMinWidth;
258  // we can move to the right until we consume all space of the
259  // resiable column
260  m_RightLimit = m_OrigPos + resizable_width - kMinWidth;
261  } else {
262  // we can move to the left until we consume all space of the
263  // resiable column
264  m_LeftLimit = m_OrigPos - resizable_width + kMinWidth;
265  // we can move to the right until the resized column width is >= 0
266  //TVPRect rc = m_Host->HHH_GetHeaderRect();
267  //m_RightLimit = rc.Width();
269  }
270  } else {
273  m_RightLimit = rc.Width();
274  }
276  }
277 }
278 
279 
280 // find the next visible column to the right (left)
282 {
283  int col_n = (int) m_Columns.size();
284  _ASSERT(index >= 0 && index < col_n);
285 
286  if(right) {
287  for( int i = index + 1; i < col_n; i++ ) {
288  if(m_Columns[i].m_Visible) {
289  return i;
290  }
291  }
292  } else {
293  for( int i = index - 1; i >= 0; i--) {
294  if(m_Columns[i].m_Visible) {
295  return i;
296  }
297  }
298  }
299  return -1;
300 }
301 
303 {
305 
307  int col_n = context->GetColumnsCount();
308 
309  _ASSERT(m_DragIndex < col_n);
310 
311  m_State = eMove;
313 
314  // initializing dragging state
315  m_Columns.clear();
316  m_Columns.reserve(col_n);
317  for( int i = 0; i < col_n; i++ ) {
318  m_Columns.push_back(context->GetColumn(i));
319  }
321  m_Hide = false;
322 
323  m_OrigPos = -1;
324  m_LeftLimit = m_RightLimit = -1;
325 
327 }
328 
329 
331 {
332  if(apply) {
333  switch(m_State) {
334  case eMove:
335  if(x_DoMoveColumn()) {
337  }
338  break;
339  case eResize:
341  break;
342  case ePushed:
344  break;
345  default: break;
346  }
347  }
348 
349  m_State = eIdle;
350  m_HitResult = eNone;
351  m_Columns.clear();
352 
355 }
356 
357 
359 {
360  if(m_Hide) {
361  TColumn& col = m_Columns[m_DragIndex];
362  col.m_Visible = false;
363  return true;
364  } else if(m_CurrInsIndex != m_DragIndex && m_CurrInsIndex != m_DragIndex + 1) {
366  int resizable_index = m_ResizableIndex;
367 
368  // move the column to the new position
369  m_Columns.erase(m_Columns.begin() + m_DragIndex);
370  int ins_index = m_CurrInsIndex;
372  ins_index--;
373  }
374 
375  int col_n = (int) m_Columns.size();
376  if(m_CurrInsIndex < col_n) {
377  m_Columns.insert(m_Columns.begin() + ins_index, col);
378  } else {
379  m_Columns.push_back(col);
380  }
381 
382  // update Resizable Column Index
383  if(m_DragIndex < m_ResizableIndex && m_CurrInsIndex > m_ResizableIndex ) {
384  resizable_index--;
386  resizable_index++;
387  } else if(m_DragIndex == m_ResizableIndex) {
388  resizable_index = m_CurrInsIndex;
389  }
390  resizable_index = min(resizable_index, col_n);
391 
392  m_ResizableIndex = resizable_index;
393  return true;
394  }
395  return false;
396 }
397 
398 
400 {
401  if(m_ResizableIndex >= 0 && m_ResizableIndex < (int) m_Columns.size()) {
402  return m_Columns[m_ResizableIndex].m_Visible;
403  }
404  return false;
405 }
406 
407 
408 // d_x - global shift
410 {
411  _ASSERT(m_Host);
412 
413  // calculate new separator position
414  int new_value = m_OrigPos + d_x;
415  new_value = max(new_value, m_LeftLimit);
416  new_value = min(new_value, m_RightLimit);
417 
418  if(x_HasResizableColumn()) {
421  int new_width = new_value - column.m_Pos;
422  int d_width = new_width - column.m_Width;
423 
424  if(d_width != 0) {
425  column.m_Width = new_width;
426 
427  // update columns to the left
428  for( int i = m_DragIndex + 1; i <= m_ResizableIndex; i++ ) {
429  m_Columns[i].m_Pos += d_width;
430  }
431  m_Columns[m_ResizableIndex].m_Width -= d_width;
433  }
434  } else {
435  int right_i = x_NextVisibleColumn(m_DragIndex, true);
436  TColumn& column = m_Columns[right_i];
437  int d_pos = new_value - column.m_Pos;
438 
439  if(d_pos != 0) {
440  column.m_Width -= d_pos;
441 
442  // update columns to the right
443  for( int i = right_i; i > m_ResizableIndex; i--) {
444  m_Columns[i].m_Pos += d_pos;
445  }
446  m_Columns[m_ResizableIndex].m_Width += d_pos;
448  }
449  }
450  } else { // Resizable column is hidden
452  int new_width = new_value - column.m_Pos;
453  int d_width = new_width - column.m_Width;
454 
455  if(d_width != 0) {
456  column.m_Width = new_width;
457 
458  // update columns to the right
459  for( int i = m_DragIndex + 1; i <= m_ResizableIndex; i++ ) {
460  m_Columns[i].m_Pos += d_width;
461  }
462  m_Columns[m_ResizableIndex].m_Width -= d_width;
464  }
465  }
466 }
467 
468 
470 {
471  _ASSERT(m_Host);
472 
473  TVPRect rc_header = m_Host->HHH_GetHeaderRect();
474  int d_y = m_VPMousePos.Y() - m_VPPushPos.Y();
475  m_Hide = -d_y > rc_header.Height();
476 
477  if( ! m_Hide) {
478  // locate insertion point
479  int col_n = (int) m_Columns.size();
480  int ins_pos = col_n;
481  for( int i = 0; i < col_n; i++ ) {
482  const TColumn& col = m_Columns[i];
483  if(col.m_Visible) {
484  if(m_VPMousePos.X() < (col.m_Pos + col.m_Width / 2)) {
485  ins_pos = i;
486  break;
487  }
488  }
489  }
490 
491  _ASSERT(ins_pos >=0 && ins_pos <= col_n);
492 
493  if(ins_pos != m_CurrInsIndex) {
494  m_CurrInsIndex = ins_pos;
495  }
496  }
497 
498  if(m_VPMousePos.X() != m_VPPrevMousePos.X()) {
500  }
501 }
502 
503 
505 {
506  m_CursorId = wxCURSOR_DEFAULT;
507 
508  switch(m_State) {
509  case eIdle:
510  case ePushed:
511  if(m_HitResult == eBorder) {
512  m_CursorId = wxCURSOR_SIZEWE;
513  }
514  break;
515  case eResize:
516  m_CursorId = wxCURSOR_SIZEWE;
517  break;
518  case eMove:
519  m_CursorId = wxCURSOR_SIZING;
520  break;
521  default:
522  break;
523  }
524 
526 }
527 
528 
530 {
531  if(m_State != eIdle) {
532  CGlAttrGuard guard(GL_LINE_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT );
533  x_SetupContext();
534 
535  pane.OpenPixels();
536 
537  switch(m_State) {
538  case eResize:
539  x_RenderResizeMode(pane);
540  break;
541  case eMove:
542  x_RenderMoveMode(pane);
543  break;
544  default:
545  break;
546  }
547 
548  pane.Close();
549  }
550 }
551 
552 
554 {
555  IRender& gl = GetGl();
556 
557  gl.Enable(GL_BLEND);
558  gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
559 
560  gl.Enable(GL_LINE_SMOOTH);
561  glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
562 }
563 
564 
566 {
567  IRender& gl = GetGl();
568 
569  // draw separation lines
570  const TVPRect& rc_vp = pane.GetViewport();
571  int y1 = rc_vp.Bottom();
572  int y2 = rc_vp.Top();
573 
574  gl.Color4d(0.0, 0.0, 1.0, 0.25);
575  gl.LineWidth(3.0);
576 
577  gl.Begin(GL_LINES);
578  for( size_t i = 0; i < m_Columns.size(); i++) {
579  TColumn& col = m_Columns[i];
580  if(col.m_Visible) {
581  int x = col.m_Pos + col.m_Width - 1;
582  gl.Vertex2d(x, y1);
583  gl.Vertex2d(x, y2);
584  }
585  }
586  gl.End();
587 
588  // highligh the columns being resized
589  TVPRect rc_col= m_Host->HHH_GetHeaderRect();
590 
591  if(x_HasResizableColumn()) {
592  // render the signle column
593  int i_col = (m_DragIndex < m_ResizableIndex) ? m_DragIndex
595  const TColumn& col = m_Columns[i_col];
596  rc_col.SetHorz(col.m_Pos, col.Right());
597  } else {
598  // render a rectangle covering two columns (both are affected by resiaing in this mode)
599  int x1 = m_Columns[m_DragIndex].m_Pos;
600  int right_i = x_NextVisibleColumn(m_DragIndex, true);
601  int x2 = m_Columns[right_i].Right();
602  rc_col.SetHorz(x1, x2);
603  }
604 
605  gl.Begin(GL_LINES);
606  gl.Vertex2d(rc_col.Left(), rc_col.Bottom());
607  gl.Vertex2d(rc_col.Right(), rc_col.Bottom());
608  gl.End();
609 
610  gl.LineWidth(1.0);
611 }
612 
613 
614 
615 // renders handler in eMove mode
617 {
618  _ASSERT(m_Host && m_State == eMove);
619  _ASSERT(m_CurrInsIndex >=0 && m_CurrInsIndex <= (int) m_Columns.size());
620 
621  IRender& gl = GetGl();
622 
623  const TVPRect rc_h = m_Host->HHH_GetHeaderRect();
624 
625  gl.Color4d(0.0, 0.0, 1.0, 0.25);
626  if( ! m_Hide) {
628  // highlight insertion point
629  TModelUnit x = 0;
630  if(m_CurrInsIndex < (int) m_Columns.size()) {
631  x = m_Columns[m_CurrInsIndex].m_Pos;
632  } else {
633  x = m_Columns[m_CurrInsIndex - 1].Right();
634  }
635  x -= 0.5;
636 
637  // render insertion point
638  gl.LineWidth(6);
639  gl.Begin(GL_LINES);
640  gl.Vertex2d(x, rc_h.Bottom());
641  gl.Vertex2d(x, rc_h.Top());
642  gl.End();
643  } else {
644  // highlight its current position
645  TVPRect rc_col(rc_h);
646  const TColumn& col = m_Columns[m_DragIndex];
647  rc_col.SetHorz(col.m_Pos, col.Right());
648 
649  gl.LineWidth(3);
650  gl.RectC(rc_col);
651  }
652  }
653  gl.LineWidth(1);
654 
655  // render column header
656  TVPRect rc_col(rc_h);
657  int left = m_Columns[m_DragIndex].m_Pos;
658  int right = m_Columns[m_DragIndex].Right();
659  rc_col.SetHorz(left, right);
660 
662 
664 
665  // render the cross
666  if(m_Hide) {
667  int x = m_VPMousePos.X();
668  int y = m_VPMousePos.Y();
669  int w = 16;
670 
671  gl.Color4d(1.0, 0.0, 0.0, 0.5);
672  gl.LineWidth(7);
673  gl.Begin(GL_LINES);
674  gl.Vertex2d(x - w, y - w);
675  gl.Vertex2d(x + w, y + w);
676  gl.Vertex2d(x + w, y - w);
677  gl.Vertex2d(x - w, y + w);
678  gl.End();
679  gl.LineWidth(1.0);
680  }
681 }
682 
683 
#define static
static const int kGrabZone
CAlnMultiHeaderHandler.
static const int kMinWidth
@ eNone
None specified.
Definition: blast_def.h:326
CAlnMultiHeaderHandler.
virtual wxEvtHandler * GetEvtHandler()
void OnLeftUp(wxMouseEvent &event)
void OnLeftDown(wxMouseEvent &event)
void OnKeyDown(wxKeyEvent &event)
IAMHeaderHandlerHost * m_Host
int x_NextVisibleColumn(int index, bool right)
virtual void SetPane(CGlPane *pane)
virtual void SetHost(IAMHeaderHandlerHost *host)
virtual void Render(CGlPane &pane)
void x_RenderMoveMode(CGlPane &pane)
virtual IGenericHandlerHost * GetGenericHost()
void x_RenderResizeMode(CGlPane &pane)
int m_CurrInsIndex
insertion point for the column being moved (new index for this column)
void OnMotion(wxMouseEvent &event)
EHitResult x_HitTest(const TVPPoint &point, int &index)
CGlAttrGuard - guard class for restoring OpenGL attributes.
Definition: glutils.hpp:130
class CGlPane
Definition: glpane.hpp:62
virtual IAlnMultiHeaderContext * HHH_GetContext()=0
virtual TVPRect HHH_GetHeaderRect()=0
virtual void HHH_SetColumns(const TColumns &columns, int resizable_index)=0
change order of columns, their width and posisitions
virtual void HHH_SortByColumn(int index)=0
virtual TVPPoint HHH_GetVPPosByWindowPos(const wxPoint &pos)=0
returns bounds of the Header
virtual void HHH_RenderColumnHeader(int index, const TVPRect &rc)=0
IAlnMultiHeaderContext.
virtual int GetColumnIndexByX(int x) const =0
virtual int GetColumnsCount() const =0
virtual int GetResizableColumnIndex() const =0
virtual const SColumn & GetColumn(int index) const =0
IGenericHandlerHost.
virtual void GHH_Redraw()=0
redraws the Host and the handler
virtual void GHH_SetCursor(const wxCursor &cursor)=0
changes the cursor in the hosting window
#define NULL
Definition: ncbistd.hpp:225
GLdouble TModelUnit
Definition: gltypes.hpp:48
T X() const
Definition: glpoint.hpp:59
T Height() const
Definition: glrect.hpp:90
virtual void Enable(GLenum glstate)=0
virtual void Begin(GLenum mode)=0
Start rendering.
T Top() const
Definition: glrect.hpp:84
virtual void BlendFunc(GLenum sfactor, GLenum dfactor)=0
Options to be used when GL_BLEND is enabled.
T Bottom() const
Definition: glrect.hpp:82
void Offset(T d_x, T d_y)
Definition: glrect.hpp:186
T Width() const
Definition: glrect.hpp:86
bool OpenPixels()
Definition: glpane.hpp:432
IRender & GetGl()
convenience function for getting current render manager
void RectC(const TVPRect &rc)
Definition: irender.hpp:197
void Vertex2d(GLdouble x, GLdouble y)
Definition: irender.hpp:185
T Right() const
Definition: glrect.hpp:83
TVPRect & GetViewport(void)
Definition: glpane.hpp:332
void Color4d(GLdouble r, GLdouble g, GLdouble b, GLdouble a)
Definition: irender.hpp:102
T Left() const
Definition: glrect.hpp:81
T Y() const
Definition: glpoint.hpp:60
virtual void End()=0
Finish rendering (create buffer and send to renderer)
void Close(void)
Definition: glpane.cpp:178
virtual void LineWidth(GLfloat w)=0
Set line width for drawing: glLineWidth()
void SetHorz(T left, T right)
Definition: glrect.hpp:117
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
unsigned int
A callback function used to compare two keys in a database.
Definition: types.hpp:1210
END_EVENT_TABLE()
int i
const struct ncbi::grid::netcache::search::fields::KEY key
T max(T x_, T y_)
T min(T x_, T y_)
@ eIdle
static const char * column
Definition: stats.c:23
SColumn describes a single column.
int m_Width
horizontal position in viewport
bool m_Visible
can be used to identify column
#define _ASSERT
#define const
Definition: zconf.h:230
Modified on Sat Dec 09 04:45:27 2023 by modify_doxy.py rev. 669887