/** \file * \brief iupmatrix control * scrolling * * See Copyright Notice in "iup.h" */ #include "iup.h" #include "iupcbs.h" #include #include "iup_object.h" #include "iup_stdcontrols.h" #include "iupmat_def.h" #include "iupmat_scroll.h" #include "iupmat_focus.h" #include "iupmat_aux.h" #include "iupmat_edit.h" #include "iupmat_draw.h" /* Macros used by "dir" parameter of the iMatrixScrollLine and iMatrixScrollColumn functions */ #define IMAT_SCROLL_LEFT 0 #define IMAT_SCROLL_RIGHT 1 #define IMAT_SCROLL_UP 2 #define IMAT_SCROLL_DOWN 3 /**************************************************************************/ /* Private functions */ /**************************************************************************/ static void iMatrixScrollToVisible(ImatLinColData* p, int index) { /* The work here is just to position first and first_offset, so "index" is between "first" and "last". */ /* It is called only for discrete scrolling, so first_offset usually will be set to 0. */ /* already visible, change nothing */ if (index > p->first && index < p->last) return; /* scroll to visible, means position the cell so the start at left is visible */ if (index <= p->first) { p->first = index; p->first_offset = 0; return; } else /* (index >= p->last) */ { p->last = index; /* adjust "first" according to "last" */ iupMatrixAuxAdjustFirstFromLast(p); } } /* Callback to report to the user which visualization area of the matrix changed. */ static void iMatrixScrollCallScrollTopCb(Ihandle* ih) { IFnii cb = (IFnii)IupGetCallback(ih, "SCROLLTOP_CB"); if (cb) cb(ih, ih->data->lines.first, ih->data->columns.first); } static int iMatrixScrollGetNextNonEmpty(Ihandle* ih, int m, int index) { ImatLinColData* p; if (m == IMAT_PROCESS_LIN) p = &(ih->data->lines); else p = &(ih->data->columns); while(index < p->num && p->sizes[index] == 0) index++; if (index > p->num-1) { if (p->num == 1) return 1; else return p->num-1; } else return index; } static int iMatrixScrollGetPrevNonEmpty(Ihandle* ih, int m, int index) { ImatLinColData* p; if (m == IMAT_PROCESS_LIN) p = &(ih->data->lines); else p = &(ih->data->columns); while(index > 0 && p->sizes[index] == 0) index--; if (index < 1) return 1; else return index; } static void iMatrixScrollSetFocusScrollToVisible(Ihandle* ih, int lin, int col) { /* moving focus and eventually scrolling */ iupMatrixFocusSet(ih, lin, col); /* set for both because focus maybe hidden */ iMatrixScrollToVisible(&ih->data->columns, ih->data->columns.focus_cell); iMatrixScrollToVisible(&ih->data->lines, ih->data->lines.focus_cell); } static void iMatrixScrollSetFocusScrollToVisibleLinCol(Ihandle* ih, int m, int index) { if (m == IMAT_PROCESS_COL) iMatrixScrollSetFocusScrollToVisible(ih, ih->data->lines.focus_cell, index); else iMatrixScrollSetFocusScrollToVisible(ih, index, ih->data->columns.focus_cell); } /**************************************************************************/ /* Exported functions */ /**************************************************************************/ void iupMatrixScrollToVisible(Ihandle* ih, int lin, int col) { int old_lines_first = ih->data->lines.first; int old_columns_first = ih->data->columns.first; int old_lines_first_offset = ih->data->lines.first_offset; int old_columns_first_offset = ih->data->columns.first_offset; iMatrixScrollToVisible(&ih->data->columns, col); iMatrixScrollToVisible(&ih->data->lines, lin); if ((ih->data->lines.first != old_lines_first || ih->data->lines.first_offset != old_lines_first_offset) || (ih->data->columns.first != old_columns_first || ih->data->columns.first_offset != old_columns_first_offset)) { /* when "first" is changed must update scroll pos */ if (ih->data->columns.first != old_columns_first || ih->data->columns.first_offset != old_columns_first_offset) iupMatrixAuxUpdateScrollPos(ih, IMAT_PROCESS_COL); if (ih->data->lines.first != old_lines_first || ih->data->lines.first_offset != old_lines_first_offset) iupMatrixAuxUpdateScrollPos(ih, IMAT_PROCESS_LIN); iMatrixScrollCallScrollTopCb(ih); iupMatrixDraw(ih, 1); } } void iupMatrixScrollMove(iupMatrixScrollMoveFunc func, Ihandle* ih, int mode, float pos, int m) { int old_lines_first = ih->data->lines.first; int old_columns_first = ih->data->columns.first; int old_lines_first_offset = ih->data->lines.first_offset; int old_columns_first_offset = ih->data->columns.first_offset; iupMatrixEditForceHidden(ih); func(ih, mode, pos, m); if ((ih->data->lines.first != old_lines_first || ih->data->lines.first_offset != old_lines_first_offset) || (ih->data->columns.first != old_columns_first || ih->data->columns.first_offset != old_columns_first_offset)) { /* when "first" is changed must update scroll pos */ if (ih->data->columns.first != old_columns_first || ih->data->columns.first_offset != old_columns_first_offset) iupMatrixAuxUpdateScrollPos(ih, IMAT_PROCESS_COL); if (ih->data->lines.first != old_lines_first || ih->data->lines.first_offset != old_lines_first_offset) iupMatrixAuxUpdateScrollPos(ih, IMAT_PROCESS_LIN); iMatrixScrollCallScrollTopCb(ih); iupMatrixDraw(ih, 0); } } /************************************************************************************/ /* This function is called when the "home" key is pressed. In the first time, go to the beginning of the line. In the second time, go to the beginning of the page. In the third time, go to the beginning of the matrix. -> mode and pos : DO NOT USED. */ void iupMatrixScrollHomeFunc(Ihandle* ih, int unused_mode, float unused_pos, int unused_m) { (void)unused_m; (void)unused_mode; (void)unused_pos; /* called only for mode==IMAT_SCROLLKEY */ /* moving focus and eventually scrolling */ if(ih->data->homekeycount == 0) /* go to the beginning of the line */ { int col = iMatrixScrollGetNextNonEmpty(ih, IMAT_PROCESS_COL, 1); iMatrixScrollSetFocusScrollToVisibleLinCol(ih, IMAT_PROCESS_COL, col); } else if(ih->data->homekeycount == 1) /* go to the beginning of the visible page */ { iMatrixScrollSetFocusScrollToVisible(ih, ih->data->lines.first, ih->data->columns.first); } else if(ih->data->homekeycount == 2) /* go to the beginning of the matrix 1:1 */ { int lin = iMatrixScrollGetNextNonEmpty(ih, IMAT_PROCESS_LIN, 1); int col = iMatrixScrollGetNextNonEmpty(ih, IMAT_PROCESS_COL, 1); iMatrixScrollSetFocusScrollToVisible(ih, lin, col); } } /* This function is called when the "end" key is pressed. In the first time, go to the end of the line. In the second time, go to the end of the page. In the third time, go to the end of the matrix. -> mode and pos : DO NOT USED. */ void iupMatrixScrollEndFunc(Ihandle* ih, int unused_mode, float unused_pos, int unused_m) { (void)unused_m; (void)unused_mode; (void)unused_pos; /* called only for mode==IMAT_SCROLLKEY */ /* moving focus and eventually scrolling */ if(ih->data->endkeycount == 0) /* go to the end of the line */ { int col = iMatrixScrollGetPrevNonEmpty(ih, IMAT_PROCESS_COL, ih->data->columns.num-1); iMatrixScrollSetFocusScrollToVisibleLinCol(ih, IMAT_PROCESS_COL, col); } else if(ih->data->endkeycount == 1) /* go to the end of the visible page */ { iMatrixScrollSetFocusScrollToVisible(ih, ih->data->lines.last, ih->data->columns.last); } else if(ih->data->endkeycount == 2) /* go to the end of the matrix */ { int lin = iMatrixScrollGetPrevNonEmpty(ih, IMAT_PROCESS_LIN, ih->data->lines.num-1); int col = iMatrixScrollGetPrevNonEmpty(ih, IMAT_PROCESS_COL, ih->data->columns.num-1); iMatrixScrollSetFocusScrollToVisible(ih, lin, col); } } /* This function is called to move a cell to the left or up. -> mode : indicate if the command was from the keyboard or the scrollbar. If scrollbar, do not change the focus. -> pos : DO NOT USED -> m : define the mode of operation: lines or columns [IMAT_PROCESS_LIN|IMAT_PROCESS_COL] */ void iupMatrixScrollLeftUpFunc(Ihandle* ih, int mode, float pos, int m) { ImatLinColData* p; (void)pos; if(m == IMAT_PROCESS_LIN) p = &(ih->data->lines); else p = &(ih->data->columns); if (mode == IMAT_SCROLLKEY) { /* moving focus and eventually scrolling */ int next = iMatrixScrollGetPrevNonEmpty(ih, m, p->focus_cell-1); iMatrixScrollSetFocusScrollToVisibleLinCol(ih, m, next); } else /* IMAT_SCROLLBAR */ { /* always scrolling without changing focus */ p->first = iMatrixScrollGetPrevNonEmpty(ih, m, p->first-1); p->first_offset = 0; } } /* This function is called to move a cell to the right or down. -> mode : indicate if the command from the keyboard or the scrollbar. If scrollbar, do not change the focus. -> pos : DO NOT USED -> m : define the mode of operation: lines or columns [IMAT_PROCESS_LIN|IMAT_PROCESS_COL] */ void iupMatrixScrollRightDownFunc(Ihandle* ih, int mode, float pos, int m) { ImatLinColData* p; (void)pos; if(m == IMAT_PROCESS_LIN) p = &(ih->data->lines); else p = &(ih->data->columns); if (mode == IMAT_SCROLLKEY) { /* moving focus and eventually scrolling */ int next = iMatrixScrollGetNextNonEmpty(ih, m, p->focus_cell+1); iMatrixScrollSetFocusScrollToVisibleLinCol(ih, m, next); } else /* IMAT_SCROLLBAR */ { /* always scrolling without changing focus */ p->first = iMatrixScrollGetNextNonEmpty(ih, m, p->first+1); p->first_offset = 0; } } /* This function is called to move a page to the left or up. -> mode : indicate if the command was from the keyboard or the scrollbar. If scrollbar, do not change the focus. -> pos : DO NOT USED -> m : define the mode of operation: lines (PgLeft) or columns (PgUp) [IMAT_PROCESS_LIN|IMAT_PROCESS_COL] */ void iupMatrixScrollPgLeftUpFunc(Ihandle* ih, int mode, float pos, int m) { ImatLinColData* p; (void)pos; if(m == IMAT_PROCESS_LIN) p = &(ih->data->lines); else p = &(ih->data->columns); if (mode == IMAT_SCROLLKEY) { /* moving focus and eventually scrolling */ int next = iMatrixScrollGetPrevNonEmpty(ih, m, p->focus_cell - (p->last - p->first)); iMatrixScrollSetFocusScrollToVisibleLinCol(ih, m, next); } else /* IMAT_SCROLLBAR */ { /* always scrolling without changing focus */ p->first = iMatrixScrollGetPrevNonEmpty(ih, m, p->first - (p->last - p->first)); p->first_offset = 0; } } /* This function is called to move a page to the right or down. -> mode : indicate if the command was from the keyboard or the scrollbar. If scrollbar, do not change the focus. -> pos : DO NOT USED -> m : define the mode of operation: lines (PgDown) or columns (PgRight) [IMAT_PROCESS_LIN|IMAT_PROCESS_COL] */ void iupMatrixScrollPgRightDownFunc(Ihandle* ih, int mode, float pos, int m) { ImatLinColData* p; (void)pos; if(m == IMAT_PROCESS_LIN) p = &(ih->data->lines); else p = &(ih->data->columns); if (mode == IMAT_SCROLLKEY) { /* moving focus and eventually scrolling */ int next = iMatrixScrollGetNextNonEmpty(ih, m, p->focus_cell + (p->last - p->first)); iMatrixScrollSetFocusScrollToVisibleLinCol(ih, m, next); } else /* IMAT_SCROLLBAR */ { /* always scrolling without changing focus */ p->first = iMatrixScrollGetPrevNonEmpty(ih, m, p->first + (p->last - p->first)); p->first_offset = 0; } } void iupMatrixScrollCrFunc(Ihandle* ih, int unused_mode, float unused_pos, int unused_m) { int oldlin = ih->data->lines.focus_cell; int oldcol = ih->data->columns.focus_cell; (void)unused_m; (void)unused_mode; (void)unused_pos; /* called only for mode==IMAT_SCROLLKEY */ /* try the normal processing of next cell down */ iupMatrixScrollRightDownFunc(ih, IMAT_SCROLLKEY, 0, IMAT_PROCESS_LIN); if(ih->data->lines.focus_cell == oldlin && ih->data->columns.focus_cell == oldcol) { /* If focus was not changed, it was because it is in the last line of the column. Go to the next column of the same line. */ iupMatrixScrollRightDownFunc(ih, IMAT_SCROLLKEY, 0, IMAT_PROCESS_COL); } } /* This function is called when a drag is performed in the scrollbar. -> x : scrollbar thumb position, value between 0 and 1 -> mode : DO NOT USED -> m : define the mode of operation: lines or columns [IMAT_PROCESS_LIN|IMAT_PROCESS_COL] */ void iupMatrixScrollPosFunc(Ihandle* ih, int mode, float pos, int m) { int scroll_pos; ImatLinColData* p; (void)mode; if (m == IMAT_PROCESS_LIN) p = &(ih->data->lines); else p = &(ih->data->columns); if (p->num == 1) { p->first = 1; p->first_offset = 0; return; } scroll_pos = (int)(pos * p->total_size + 0.5); /* position first and first_offset, according to scroll pos */ iupMatrixAuxAdjustFirstFromScrollPos(p, scroll_pos); } /************************************************************************************/ int iupMatrixScroll_CB(Ihandle* ih, int action, float posx, float posy) { if (!iupMatrixIsValid(ih, 0)) return IUP_DEFAULT; switch(action) { case IUP_SBUP : iupMATRIX_ScrollUp(ih); break; case IUP_SBDN : iupMATRIX_ScrollDown(ih); break; case IUP_SBPGUP : iupMATRIX_ScrollPgUp(ih); break; case IUP_SBPGDN : iupMATRIX_ScrollPgDown(ih); break; case IUP_SBRIGHT : iupMATRIX_ScrollRight(ih); break; case IUP_SBLEFT : iupMATRIX_ScrollLeft(ih); break; case IUP_SBPGRIGHT : iupMATRIX_ScrollPgRight(ih); break; case IUP_SBPGLEFT : iupMATRIX_ScrollPgLeft(ih); break; case IUP_SBPOSV : iupMATRIX_ScrollPosVer(ih,posy); break; case IUP_SBPOSH : iupMATRIX_ScrollPosHor(ih,posx); break; case IUP_SBDRAGV : iupMATRIX_ScrollPosVer(ih,posy); break; case IUP_SBDRAGH : iupMATRIX_ScrollPosHor(ih,posx); break; } iupMatrixDrawUpdate(ih); return IUP_DEFAULT; }