mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
implement cell set_value callback
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@497 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
d63add89bf
commit
901c8180ae
@ -4,6 +4,8 @@
|
||||
|
||||
#include "basiccell.h"
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
BasicCell * xaccMallocBasicCell (void)
|
||||
{
|
||||
BasicCell * cell;
|
||||
@ -12,12 +14,15 @@ BasicCell * xaccMallocBasicCell (void)
|
||||
return cell;
|
||||
}
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
void xaccInitBasicCell (BasicCell *cell)
|
||||
{
|
||||
cell->input_output = 1;
|
||||
cell->width = 0;
|
||||
cell->alignment = 0;
|
||||
cell->value = 0x0;
|
||||
cell->set_value = NULL;
|
||||
cell->enter_cell = NULL;
|
||||
cell->modify_verify = NULL;
|
||||
cell->leave_cell = NULL;
|
||||
@ -27,10 +32,23 @@ void xaccInitBasicCell (BasicCell *cell)
|
||||
cell->gui_private = NULL;
|
||||
}
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
void xaccSetBasicCellValue (BasicCell *cell, const char *val)
|
||||
{
|
||||
if (cell->value) free (cell->value);
|
||||
cell->value = strdup (val);
|
||||
void (*cb) (struct _BasicCell *, const char *);
|
||||
|
||||
cb = cell->set_value;
|
||||
if (cb) {
|
||||
/* avoid recursion by disabling the
|
||||
* callback while it'sbeing called. */
|
||||
cell->set_value = NULL;
|
||||
(*cb) (cell, val);
|
||||
cell->set_value = cb;
|
||||
} else {
|
||||
if (cell->value) free (cell->value);
|
||||
cell->value = strdup (val);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------ end of file ---------------------- */
|
||||
/* ================== end of file ====================== */
|
||||
|
@ -3,44 +3,70 @@
|
||||
* basiccell.h
|
||||
*
|
||||
* FUNCTION:
|
||||
* The BasicCell struct provides an abstract base class
|
||||
* The BasicCell class provides an abstract base class
|
||||
* defining the handling of the editing of a cell of a table.
|
||||
* Classes that provide the actual handling for different
|
||||
* cell types should inherit from this class.
|
||||
*
|
||||
* The BasicCell class encapsulates a single string value
|
||||
* which can be set & read by the programmer, and edited
|
||||
* by the "user". In the text below, the "user" is the
|
||||
* person controlling the mouse and keyboard. Thus, when
|
||||
* the user makes a move, it means that they have somehow
|
||||
* intereacted with the cell, by clikcing with mouse or by
|
||||
* typing at the keyboard. This class provides three
|
||||
* callbacks which allow the programmer to understand what
|
||||
* the user is doing.
|
||||
*
|
||||
* The programmer can create a custom GUI for editing the
|
||||
* contents of the cell. There are three callbacks to allow
|
||||
* a custom GUI to be created, destroyed and moved about.
|
||||
*
|
||||
* Since this class is implemented in C not C++, there is
|
||||
* a "minor" problem with inheritance. To emulate the
|
||||
* overloading of a virtual "SetValues" method, there is
|
||||
* a set_value() callback, which will be called whenever
|
||||
* the xaccSetBasicCellValue() subroutine is called.
|
||||
*
|
||||
* VIRTUAL/OVERLOADED METHODS:
|
||||
* The set_value() callback will be called whenever the
|
||||
* xaccSetBasicCellValue() method is called. Derived
|
||||
* classes should provide a callback here if they need
|
||||
* to understand special cell formats.
|
||||
*
|
||||
* MEMBERS:
|
||||
* The input_output member is zero if the cell is supposed
|
||||
* to only display values, but not accept user input. If
|
||||
* non-zero, then the callbacks below are used to when the
|
||||
* cell is entered.
|
||||
*
|
||||
* CALLBACKS:
|
||||
*
|
||||
* USER CALLBACKS:
|
||||
* The enter_cell() callback is called when the user first
|
||||
* makes a move to enter a cell. The current value of the
|
||||
* cell is passed as the argument. If the callback wishes
|
||||
* to change the value of the cell, it can return a non-null
|
||||
* string. Alternately, to leave the value of the cell
|
||||
* unchanged, it can return NULL. If a string is returned,
|
||||
* the string must be as the result of a malloc.
|
||||
* makes a move to enter a cell. The current value of the
|
||||
* cell is passed as the argument. If the callback wishes
|
||||
* to change the value of the cell, it can return a non-null
|
||||
* string. Alternately, to leave the value of the cell
|
||||
* unchanged, it can return NULL. If a string is returned,
|
||||
* the string must be as the result of a malloc.
|
||||
*
|
||||
* The leave_cell() callback is called when the user exits
|
||||
* a cell. The current value of the cell is passed as the
|
||||
* argument. If the callback wishes to change the value of
|
||||
* the cell, it can return a non-null string. Alternately,
|
||||
* to leave the value of the cell unchanged, it can return
|
||||
* NULL. If a string is returned, the string must be as the
|
||||
* result of a malloc.
|
||||
* a cell. The current value of the cell is passed as the
|
||||
* argument. If the callback wishes to change the value of
|
||||
* the cell, it can return a non-null string. Alternately,
|
||||
* to leave the value of the cell unchanged, it can return
|
||||
* NULL. If a string is returned, the string must be as the
|
||||
* result of a malloc.
|
||||
*
|
||||
* The modify-verify callback is called when a user
|
||||
* makes a change to a cell.
|
||||
* The three arguments passed in are :
|
||||
* "old", the string prior to user's attempted modification,
|
||||
* "add", the string the user is attemptiong to add
|
||||
* (will be null if text is being deleted).
|
||||
* "new", the string that would result is user's changes
|
||||
* are accepted.
|
||||
* It must return a string, or void if it rejects the change.
|
||||
* The returned string will be used to update the cell value.
|
||||
* The modify_verify() callback is called when a user makes a
|
||||
* change to a cell. The three arguments passed in are :
|
||||
* "old", the string prior to user's attempted modification,
|
||||
* "add", the string the user is attemptiong to add
|
||||
* (will be null if text is being deleted).
|
||||
* "new", the string that would result is user's changes
|
||||
* are accepted.
|
||||
* It must return a string, or void if it rejects the change.
|
||||
* The returned string will be used to update the cell value.
|
||||
*
|
||||
* Some memory management rules:
|
||||
* (1) the callback must not modify the values of old, change, new
|
||||
@ -51,12 +77,32 @@
|
||||
* malloc the memory for a new string. It does not need
|
||||
* to worry about garbage collection.
|
||||
*
|
||||
* GUI STUFF:
|
||||
* The realize callback will be called when GUI-specific
|
||||
* initalization needs to be done. For Xt/Motif, the second
|
||||
* argument will be cast to the parent widget.
|
||||
*
|
||||
* HACK ALERT NOTES:
|
||||
* GUI CALLBACKS:
|
||||
* The cell may have some specific GUI elements which need
|
||||
* to be initialized/positioned/etc. There are three GUI
|
||||
* callbacks that allow the programmer to perform GUI-specific
|
||||
* initialization & changes.
|
||||
*
|
||||
* The realize() callback will be called when GUI-specific
|
||||
* initalization needs to be done. For Xt/Motif, the second
|
||||
* argument will be cast to the parent widget. The third
|
||||
* argument passes in the desired pixel-width for the GUI
|
||||
* element. (Yes, the pixel-size thing is a hack that we
|
||||
* allow for the moment. See below for more info.)
|
||||
*
|
||||
* The destroy() callback will be called when the GUI associated
|
||||
* with the cell needs to be destroyed.
|
||||
*
|
||||
* The move() callback will be called when the GUI element needs
|
||||
* to be positioned to a new location within the table grid.
|
||||
* The second and third arguments are the physical (not virtual)
|
||||
* row and column that the GUI elemnt should be moved to.
|
||||
*
|
||||
* The gui_private member may be used by the derived class to
|
||||
* store any additional GUI-specific data.
|
||||
*
|
||||
* GUI HACK ALERT NOTES:
|
||||
* The realize method takes a width argument only as a hack
|
||||
* to work around the fact that the combo-box requires a width
|
||||
* in pixels, rather than in characters. It would be nice if
|
||||
@ -74,24 +120,26 @@ typedef struct _BasicCell {
|
||||
|
||||
char * value; /* current value */
|
||||
|
||||
const char * (*enter_cell) (struct _BasicCell *,
|
||||
const char * current);
|
||||
/* "virtual", overloaded set-value method */
|
||||
void (*set_value) (struct _BasicCell *,
|
||||
const char * new_value);
|
||||
|
||||
/* cell-editing callbacks */
|
||||
const char * (*enter_cell) (struct _BasicCell *,
|
||||
const char * current);
|
||||
const char * (*modify_verify) (struct _BasicCell *,
|
||||
const char *old,
|
||||
const char *add,
|
||||
const char *new);
|
||||
const char * (*leave_cell) (struct _BasicCell *,
|
||||
const char * current);
|
||||
const char * (*leave_cell) (struct _BasicCell *,
|
||||
const char * current);
|
||||
|
||||
/* private, GUI-specific initializer */
|
||||
/* private, GUI-specific callbacks */
|
||||
void (* realize) (struct _BasicCell *,
|
||||
void *gui_handle,
|
||||
int pixel_width);
|
||||
|
||||
/* private, GUI-specific callback to move gui element */
|
||||
void (* move) (struct _BasicCell *, int phys_row, int phys_col);
|
||||
|
||||
/* private, GUI-specific callback to detroy gui element */
|
||||
void (* move) (struct _BasicCell *,
|
||||
int phys_row, int phys_col);
|
||||
void (* destroy) (struct _BasicCell *);
|
||||
|
||||
/* general hook for gui-private data */
|
||||
|
@ -24,9 +24,14 @@ static void dropDownCB (Widget w, XtPointer cd, XtPointer cb );
|
||||
static void realizeCombo (struct _BasicCell *bcell, void *w, int width);
|
||||
static void moveCombo (struct _BasicCell *bcell, int phys_row, int phys_col);
|
||||
static void destroyCombo (struct _BasicCell *bcell);
|
||||
static void setComboValue (struct _BasicCell *bcell, const char *value);
|
||||
static const char * enterCombo (struct _BasicCell *bcell, const char *value);
|
||||
static const char * leaveCombo (struct _BasicCell *bcell, const char *value);
|
||||
|
||||
#define SET(cell,str) { \
|
||||
if ((cell)->value) free ((cell)->value); \
|
||||
(cell)->value = strdup (str); \
|
||||
}
|
||||
|
||||
/* =============================================== */
|
||||
|
||||
@ -42,6 +47,7 @@ void xaccInitComboCell (ComboCell *cell)
|
||||
{
|
||||
xaccInitBasicCell ( &(cell->cell));
|
||||
cell->cell.realize = realizeCombo;
|
||||
cell->cell.set_value = setComboValue;
|
||||
cell->menuitems = (char **) malloc (sizeof (char *));
|
||||
cell->menuitems[0] = NULL;
|
||||
}
|
||||
@ -97,7 +103,7 @@ xaccSetComboCellValue (ComboCell *cell, const char * str)
|
||||
{
|
||||
PopBox * box;
|
||||
|
||||
xaccSetBasicCellValue (&(cell->cell), str);
|
||||
SET (&(cell->cell), str);
|
||||
box = (PopBox *) (cell->cell.gui_private);
|
||||
|
||||
if (str) {
|
||||
@ -117,6 +123,15 @@ xaccSetComboCellValue (ComboCell *cell, const char * str)
|
||||
|
||||
/* =============================================== */
|
||||
|
||||
static void
|
||||
setComboValue (struct _BasicCell *_cell, const char *str)
|
||||
{
|
||||
ComboCell * cell = (ComboCell *) _cell;
|
||||
xaccSetComboCellValue (cell, str);
|
||||
}
|
||||
|
||||
/* =============================================== */
|
||||
|
||||
static
|
||||
void realizeCombo (struct _BasicCell *bcell, void *w, int pixel_width)
|
||||
{
|
||||
@ -341,6 +356,12 @@ static void selectCB (Widget w, XtPointer cd, XtPointer cb )
|
||||
cell = (ComboCell *) cd;
|
||||
box = (PopBox *) (cell->cell.gui_private);
|
||||
|
||||
if ((0 > box->currow) || (0 > box->curcol)) {
|
||||
/*
|
||||
printf ("Internal Error: ComboBox: incorrect cell location \n");
|
||||
return;
|
||||
*/
|
||||
}
|
||||
/* check the reason, because the unslect callback
|
||||
* doesn't even have a value field! */
|
||||
if ( (XmCR_SINGLE_SELECT == selection->reason) ||
|
||||
@ -349,9 +370,9 @@ static void selectCB (Widget w, XtPointer cd, XtPointer cb )
|
||||
}
|
||||
if (!choice) choice = XtNewString ("");
|
||||
|
||||
printf ("combo selectcb choice %s at %d %d \n", choice, box->currow, box->curcol);
|
||||
XbaeMatrixSetCell (box->parent, box->currow, box->curcol, choice);
|
||||
xaccSetBasicCellValue (&(cell->cell), choice);
|
||||
printf ("celectcb choice %s \n", choice);
|
||||
SET (&(cell->cell), choice);
|
||||
XtFree (choice);
|
||||
|
||||
/* a diffeent way of getting the user's selection ... */
|
||||
|
@ -1,10 +1,25 @@
|
||||
|
||||
/*
|
||||
* FILE:
|
||||
* datecell.c
|
||||
*
|
||||
* HISTORY:
|
||||
* Copyright (c) 1998 Linas Vepstas
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "basiccell.h"
|
||||
#include "datecell.h"
|
||||
|
||||
static void setDateCellValue (struct _BasicCell *, const char *);
|
||||
|
||||
#define SET(cell,str) { \
|
||||
if ((cell)->value) free ((cell)->value); \
|
||||
(cell)->value = strdup (str); \
|
||||
}
|
||||
|
||||
#define DATE_SEP '/'
|
||||
|
||||
/* ================================================ */
|
||||
@ -256,7 +271,9 @@ DateMV (struct _BasicCell *_cell,
|
||||
|
||||
prtDate (buff, date->tm_mday, date->tm_mon+1, date->tm_year+1900);
|
||||
|
||||
xaccSetBasicCellValue (&(cell->cell), buff);
|
||||
if (cell->cell.value) free (cell->cell.value);
|
||||
cell->cell.value = strdup (buff);
|
||||
|
||||
datestr = strdup (buff);
|
||||
|
||||
return datestr;
|
||||
@ -279,7 +296,9 @@ DateLeave (struct _BasicCell *_cell, const char * curr)
|
||||
cell->date.tm_mon+1,
|
||||
cell->date.tm_year+1900);
|
||||
|
||||
xaccSetBasicCellValue (&(cell->cell), buff);
|
||||
if (cell->cell.value) free (cell->cell.value);
|
||||
cell->cell.value = strdup (buff);
|
||||
|
||||
retval = strdup (buff);
|
||||
return retval;
|
||||
}
|
||||
@ -312,11 +331,12 @@ xaccInitDateCell (DateCell *cell)
|
||||
cell->date = *now;
|
||||
prtDate (buff, now->tm_mday, now->tm_mon+1, now->tm_year+1900);
|
||||
|
||||
xaccSetBasicCellValue (&(cell->cell), buff);
|
||||
SET (&(cell->cell), buff);
|
||||
|
||||
cell->cell.enter_cell = DateEnter;
|
||||
cell->cell.modify_verify = DateMV;
|
||||
cell->cell.leave_cell = DateLeave;
|
||||
cell->cell.set_value = setDateCellValue;
|
||||
}
|
||||
|
||||
/* ================================================ */
|
||||
@ -337,7 +357,27 @@ xaccSetDateCellValue (DateCell *cell, int day, int mon, int year)
|
||||
cell->date.tm_year = dada.tm_year;
|
||||
|
||||
prtDate (buff, dada.tm_mday, dada.tm_mon+1, dada.tm_year+1900);
|
||||
xaccSetBasicCellValue (&(cell->cell), buff);
|
||||
|
||||
if (cell->cell.value) free (cell->cell.value);
|
||||
cell->cell.value = strdup (buff);
|
||||
}
|
||||
|
||||
/* ================================================ */
|
||||
|
||||
static void
|
||||
setDateCellValue (struct _BasicCell *_cell, const char *str)
|
||||
{
|
||||
DateCell *cell = (DateCell *) _cell;
|
||||
char buff[30];
|
||||
|
||||
xaccParseDate (&(cell->date), str);
|
||||
|
||||
prtDate (buff, cell->date.tm_mday,
|
||||
cell->date.tm_mon+1,
|
||||
cell->date.tm_year+1900);
|
||||
|
||||
if (cell->cell.value) free (cell->cell.value);
|
||||
cell->cell.value = strdup (buff);
|
||||
}
|
||||
|
||||
/* ============== END OF FILE ===================== */
|
||||
|
@ -1,11 +1,22 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include "basiccell.h"
|
||||
#include "pricecell.h"
|
||||
|
||||
static void PriceSetValue (struct _BasicCell *, const char *);
|
||||
static void Set (BasicCell *, const char *);
|
||||
|
||||
#define DECIMAL_PT '.'
|
||||
|
||||
|
||||
#define SET(cell,str) { \
|
||||
if ((cell)->value) free ((cell)->value); \
|
||||
(cell)->value = strdup (str); \
|
||||
}
|
||||
|
||||
/* ================================================ */
|
||||
/* This callback only allows numbers with a single
|
||||
* decimal point in them */
|
||||
@ -16,7 +27,7 @@ PriceMV (struct _BasicCell *_cell,
|
||||
const char *change,
|
||||
const char *newval)
|
||||
{
|
||||
BasicCell *cell = (BasicCell *) _cell;
|
||||
PriceCell *cell = (PriceCell *) _cell;
|
||||
|
||||
/* accept the newval string if user action was delete, etc. */
|
||||
if (change) {
|
||||
@ -33,8 +44,9 @@ PriceMV (struct _BasicCell *_cell,
|
||||
}
|
||||
}
|
||||
|
||||
/* hack alert - should parse the float pt value */
|
||||
xaccSetBasicCellValue (cell, newval);
|
||||
/* parse the float pt value and store it */
|
||||
cell->amount = xaccParseUSAmount (newval);
|
||||
SET ((&(cell->cell)), newval);
|
||||
return newval;
|
||||
}
|
||||
|
||||
@ -57,9 +69,10 @@ xaccInitPriceCell (PriceCell *cell)
|
||||
xaccInitBasicCell( &(cell->cell));
|
||||
cell->amount = 0.0;
|
||||
|
||||
xaccSetBasicCellValue ( &(cell->cell), "0.0");
|
||||
SET ( &(cell->cell), "0.0");
|
||||
|
||||
cell->cell.modify_verify = PriceMV;
|
||||
cell->cell.set_value = PriceSetValue;
|
||||
}
|
||||
|
||||
/* ================================================ */
|
||||
@ -69,7 +82,7 @@ void xaccSetPriceCellValue (PriceCell * cell, double amt)
|
||||
char buff[40];
|
||||
cell->amount = amt;
|
||||
sprintf (buff, "%.3f", amt);
|
||||
xaccSetBasicCellValue ( &(cell->cell), buff);
|
||||
SET ( &(cell->cell), buff);
|
||||
}
|
||||
|
||||
/* ================================================ */
|
||||
@ -79,7 +92,7 @@ void xaccSetAmountCellValue (PriceCell * cell, double amt)
|
||||
char buff[40];
|
||||
cell->amount = amt;
|
||||
sprintf (buff, "%.2f", amt);
|
||||
xaccSetBasicCellValue ( &(cell->cell), buff);
|
||||
SET ( &(cell->cell), buff);
|
||||
}
|
||||
|
||||
/* ================================================ */
|
||||
@ -93,13 +106,29 @@ void xaccSetDebCredCellValue (PriceCell * deb,
|
||||
|
||||
if (0.0 <= amt) {
|
||||
sprintf (buff, "%.2f", amt);
|
||||
xaccSetBasicCellValue ( &(cred->cell), buff);
|
||||
xaccSetBasicCellValue ( &(deb->cell), "");
|
||||
SET ( &(cred->cell), buff);
|
||||
SET ( &(deb->cell), "");
|
||||
} else {
|
||||
sprintf (buff, "%.2f", -amt);
|
||||
xaccSetBasicCellValue ( &(cred->cell), "");
|
||||
xaccSetBasicCellValue ( &(deb->cell), buff);
|
||||
SET ( &(cred->cell), "");
|
||||
SET ( &(deb->cell), buff);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================ */
|
||||
|
||||
static void
|
||||
PriceSetValue (struct _BasicCell *_cell, const char *str)
|
||||
{
|
||||
char buff[40];
|
||||
PriceCell *cell = (PriceCell *) _cell;
|
||||
|
||||
SET (((BasicCell *)_cell), str);
|
||||
|
||||
cell->amount = xaccParseUSAmount (str);
|
||||
|
||||
sprintf (buff, "%.2f", cell->amount);
|
||||
SET ( &(cell->cell), buff);
|
||||
}
|
||||
|
||||
/* --------------- end of file ---------------------- */
|
||||
|
@ -4,6 +4,21 @@
|
||||
#include "basiccell.h"
|
||||
#include "quickfillcell.h"
|
||||
|
||||
#define SET(cell,str) { \
|
||||
if ((cell)->value) free ((cell)->value); \
|
||||
(cell)->value = strdup (str); \
|
||||
}
|
||||
|
||||
/* ================================================ */
|
||||
|
||||
static void
|
||||
quick_set (struct _BasicCell *_cell,
|
||||
const char *val)
|
||||
{
|
||||
QuickFillCell *cell = (QuickFillCell *) _cell;
|
||||
xaccSetQuickFillCellValue (cell, val);
|
||||
}
|
||||
|
||||
/* ================================================ */
|
||||
/* when entering new cell, reset pointer to root */
|
||||
|
||||
@ -61,7 +76,7 @@ quick_modify (struct _BasicCell *_cell,
|
||||
if (cell->qf) retval = strdup (cell->qf->text);
|
||||
}
|
||||
|
||||
xaccSetBasicCellValue (&(cell->cell), retval);
|
||||
SET (&(cell->cell), retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -104,15 +119,16 @@ xaccInitQuickFillCell (QuickFillCell *cell)
|
||||
cell->cell.enter_cell = quick_enter;
|
||||
cell->cell.modify_verify = quick_modify;
|
||||
cell->cell.leave_cell = quick_leave;
|
||||
cell->cell.set_value = quick_set;
|
||||
}
|
||||
|
||||
/* ================================================ */
|
||||
|
||||
void
|
||||
xaccSetQuickFillCellValue (QuickFillCell *cell, char * value)
|
||||
xaccSetQuickFillCellValue (QuickFillCell *cell, const char * value)
|
||||
{
|
||||
xaccQFInsertText (cell->qfRoot, value);
|
||||
xaccSetBasicCellValue (&(cell->cell), value);
|
||||
SET (&(cell->cell), value);
|
||||
}
|
||||
|
||||
/* =============== END OF FILE ==================== */
|
||||
|
@ -32,7 +32,7 @@ typedef struct _QuickFillCell {
|
||||
QuickFillCell * xaccMallocQuickFillCell (void);
|
||||
void xaccInitQuickFillCell (QuickFillCell *);
|
||||
|
||||
void xaccSetQuickFillCellValue (QuickFillCell *, char *);
|
||||
void xaccSetQuickFillCellValue (QuickFillCell *, const char *);
|
||||
|
||||
#endif /* __XACC_FILL_CELL_C__ */
|
||||
|
||||
|
@ -473,7 +473,7 @@ table->current_cursor_col,
|
||||
virt_row, virt_col);
|
||||
|
||||
/* before leaving, the current virtual position,
|
||||
* commit any aedits that have been accumulated
|
||||
* commit any edits that have been accumulated
|
||||
* in the cursor */
|
||||
xaccCommitCursor (table);
|
||||
xaccMoveCursorGUI (table, virt_row, virt_col);
|
||||
|
Loading…
Reference in New Issue
Block a user