Originally I was using a Gtk::TreeView
but due to deprecation am converting to a Gtk::ColumnView
. I have worked out most of the problems, but am stuck when obtaining correct X/Y coordinates from a popup context menu on a widget within the Gtk::ColumnView
. I can use popup->get_pointing_to()
but I cannot work out how to convert that value to widget relative coordinates. In Gtk::TreeView
there were functions such as convert_widget_to_bin_window_coords()
and using a combination of them I could obtain the exact coordinate and then could easily calculate which cell in the treeview that was clicked on given that I knew the underlying sizes. This does not seem possible with a Gtk::ColumnView
in terms of member functions.
So I created the following code in an attempt to emulate what the GTK developers removed:
void RPN_Functions::Matrix_Editors_Get_Context_Row_Column(
Gtk::PopoverMenu *popup,
MatrixColumnView *matrix,
int cell_width,
int &row,
int &col
) const
{
//
//First we must validate all parameters and terminate if any fail.
//
assert(popup);
assert(matrix);
assert(cell_width > 0);
//
//Now we need to set the row and column to initial values so that if an
//error occurs we have meaningful data in the output parameters.
//
row = RPN_Matrix_Editor_Invalid_Row;
col = RPN_Matrix_Editor_Invalid_Column;
//
//Now we need to declare the rectangle to be used to contain the X and Y
//co-ordinates of the popup menu.
//
Gdk::Rectangle popup_rectangle;
//
//Now we need to fetch the rectangle that the context menu popped up at.
//This will allow us to determine the X and Y position. We also must
//check to see that the rectangle has been populated and if not the
//above row and column will be returned.
//
if (popup->get_pointing_to(popup_rectangle))
{
//
//Now that we have the popup/pointing rectangle we now need to fetch
//the X and Y co-ordinates.
//
auto popup_x = popup_rectangle.get_x();
auto popup_y = popup_rectangle.get_y();
//
//As we have set the popup menu to have a one pixel hot spot, we must
//ensure that this is true and terminate if this fails.
//
assert(popup_rectangle.get_height() == 1);
assert(popup_rectangle.get_width() == 1);
//
//Now we need to determine if the matrix has row or column (or both)
//separators so that we can adjust the heights and widths to allow
//for them.
//
auto have_column_separators = matrix->get_show_column_separators();
auto have_row_separators = matrix->get_show_row_separators();
//
//Now we need to calculate the width of a column separator and then
//set a variable so that we can use it to adjust the column data.
//
int column_separator_width;
column_separator_width = (have_column_separators ?
MyGtk::Matrix_Column_Separator_Width :
0
);
//
//Now we need to calculate the height of a row separator and then set
//a variable so that we can use it to adjust the row information.
//
int row_separator_height;
row_separator_height = (have_row_separators ?
MyGtk::Matrix_Row_Separator_Height :
0
);
//
//Now we need to fetch the pango context for the matrix so that we
//can then use this to create a pango layout.
//
auto context = matrix->get_pango_context();
assert(context);
//
//Now that we have the pango context for the matrix we can now create
//a pango layout.
//
auto layout = Pango::Layout::create(context);
assert(layout);
//
//Now we need to create a new pango font and set the name and size
//to the same values as used by the CSS that is used to format the
//headers for the columnviews.
//
Pango::FontDescription header_font;
MyGtk::Pango_Update_Font_Name(
header_font,
MyGtk::Application_Fixed_Width_Font
);
MyGtk::Pango_Update_Font_Size(
header_font,
MyGtk::Application_Fixed_Width_Font_Size
);
//
//Now that we have set the above font we must set it into the pango
//layout.
//
layout->set_font_description(header_font);
//
//Now we need to set the layout text to something so that we can then
//calculate the height of it and given that it is a fixed width font
//we know that the height fetched will be correct regardless of how
//few characters we use.
//
layout->set_text("X");
//
//Now that the layout is set we now must fetch the height of the
//layout and truncate it to an integer so that it can be added to
//the row height vector and be the same as the other data values
//that will be fetched.
//
auto layout_height =
static_cast<int>(
MyGtk::Get_Pango_Layout_Height(
layout
)
);
//
//Vector to contain the cumulative heights for each row in the matrix.
//This is then used to determine which row the user has clicked on.
//
std::vector<int> row_heights(
MyGtk::Matrix_Get_Number_Lines(matrix) + 1,
0
);
//
//Now we need to append the height of the row number as it is the
//same height as the heading which can be clicked on and must be
//taken into account.
//
row_heights[0] = layout_height + row_separator_height;
//
//Variables to contain the width and height of the first column in
//each row of the matrix. The width value is used later for the
//column data.
//
int row_number_height;
int row_number_width;
//
//Now we need to loop for all rows in the matrix and for each row we
//calculate the height of the first row and column and then accumulate
//the height information in the above vector.
//
for (int ix = 0; ix < MyGtk::Matrix_Get_Number_Lines(matrix); ++ix)
{
//
//Now we need to obtain the width and height for the row number
//column so that we can account for it when calculating the
//column information.
//
MyGtk::Matrix_Get_Column_Size(
matrix,
ix,
RPN_Row_Number_Column,
row_number_width,
row_number_height
);
//
//Now that we have the width of the column we can set the IX+1'th
//entry to the sum of the IX'th entry plus the width of this row.
//
row_heights[ix + 1] =
row_heights[ix] +
row_number_height +
row_separator_height;
}
//
//Now we need to ensure that the row heights vector has been properly
//filled. We need the '+1' as the headers for the matrix columns is
//also in the vector.
//
assert(static_cast<int>(row_heights.size()) == MyGtk::Matrix_Get_Number_Lines(matrix) + 1);
//
//Vector to contain the cumulative widths for each column in the matrix.
//This is then used to determine which cell the user has clicked on.
//
std::vector<int> column_widths(
MyGtk::Matrix_Get_Number_Columns(matrix),
0
);
//
//Now we need to set the first entry in the column widths vector which
//is the row number. We have fetched the sizing information above.
//
column_widths[0] = row_number_width + column_separator_width;
//
//Variables to contain the width and height of each of the columns
//in the first row of the matrix.
//
int column_height;
int column_width;
//
//Now we need to loop for all columns in the first row of the matrix
//so that we can then fetch and accumulate the widths. As we already
//know the width of the row number column, we start at one in the
//loop.
//
for (int ix = 1; ix < MyGtk::Matrix_Get_Number_Columns(matrix); ++ix)
{
//
//Now we need to obtain the width and height for the first column
//in an editor so that we can account for it when calculating
//the column information.
//
MyGtk::Matrix_Get_Column_Size(
matrix,
MyGtk::Matrix_First_Row,
ix,
column_width,
column_height
);
//
//Now that we have the width of the column we can set the IX'th
//entry to the sum of the IX-1'th entry plus the width of this
//column.
//
column_widths[ix] =
column_widths[ix - 1] +
column_width +
column_separator_width;
}
//
//Now we need to ensure that the column widths vector has been properly
//filled.
//
assert(static_cast<int>(column_widths.size()) == MyGtk::Matrix_Get_Number_Columns(matrix));
//
//Variables for the scrolled position of the matrix if it is within a
//scrolled window. If not the values will be set to zero.
//
double scrolled_pixels_x;
double scrolled_pixels_y;
//
//Now we need to determine if the matrix is within a scrolled window.
//
auto scrolled = MyGtk::Matrix_Get_Parent_Scrolled_Window(matrix);
//
//Now we need to double check that the matrix is within a scrolled
//window (should be if the application is designed correctly) before
//we attempt to fetch any data.
//
if (scrolled)
{
//
//Now we need to fetch the horizontal adjustment for the scrolled
//window.
//
auto h_adjustment =
MyGtk::Scrolled_Window_Get_Adjustment_Horizontal(scrolled);
assert(h_adjustment);
//
//Now we need to fetch the vertical adjustment for the scrolled
//window.
//
auto v_adjustment =
MyGtk::Scrolled_Window_Get_Adjustment_Vertical(scrolled);
assert(v_adjustment);
//
//Now we need to fetch the upper limits for the horizontal and
//vertical adjustments.
//
auto h_adjustment_upper = h_adjustment->get_upper();
auto v_adjustment_upper = v_adjustment->get_upper();
//
//Now we need to calculate the total range of the scrolled window
//in the horizontal direction.
//
auto scrolled_range_x =
h_adjustment_upper - h_adjustment->get_lower();
//
//Now we need to calculate the total range of the scrolled window
//in the vertical direction.
//
auto scrolled_range_y =
v_adjustment_upper - v_adjustment->get_lower();
//
//Now we need to fetch the current X and Y co-ordinates where the
//scrolled window is at.
//
auto scrolled_value_x = h_adjustment->get_value();
auto scrolled_value_y = v_adjustment->get_value();
//
//Now we need to calculate the percentage of the scroll bar with
//respect to the current position and the range of the scroll bar.
//
auto scrolled_percent_x = scrolled_value_x / scrolled_range_x;
auto scrolled_percent_y = scrolled_value_y / scrolled_range_y;
//
//Now we need to calculate how many pixels in the X direction
//that the window has been scrolled by.
//
scrolled_pixels_x =
(1 + scrolled_percent_x) *
popup_x;
//
//Now we need to calculate how many pixels in the Y direction
//that the window has been scrolled by.
//
scrolled_pixels_y =
(1 + scrolled_percent_y) *
popup_y;
}
else
{
//
//As the matrix is not within a scrolled window then we must set
//the scrolled pixels values to the value of the popup rectangle.
//We do this due to the way we calculate the values when the matrix
//is in a scrolled window.
//
scrolled_pixels_x = popup_x;
scrolled_pixels_y = popup_y;
}
//
//Now that we know the X co-ordinate of the scroll point we now need
//to calculate the number of columns that this represents. We do
//this by looping through the accumulated column widths and exit the
//loop when we find a value in the range of the pixel position.
//
for (size_t ix = 1; ix < column_widths.size(); ++ix)
{
//
//As we are commencing at one we know that we can safely compare
//the IX-1'th and the IX'th widths so that we can determine where
//the scrolled X pixels value fits and thus determine the column
//clicked on.
//
printf("Col - X = %g, wid[%lu]=%d, wid[%lu]=%d, popup=%d\n",
scrolled_pixels_x, ix - 1, column_widths[ix-1], ix, column_widths[ix], popup_x);
if (scrolled_pixels_x >= column_widths[ix - 1] &&
scrolled_pixels_x <= column_widths[ix])
{
//
//Now that we know that the clicked on column is IX we must
//set the COL variable appropriately. As the first column
//that has user entered data in it is one then we must set
//it accordingly.
//
col = ix;
break;
}
}
//
//Now that we know the Y co-ordinate of the scroll point we now need
//to calculate the number of columns that this represents. We do
//this by looping through the accumulated row heights and exit the
//loop when we find a value in the range of the pixel position.
//
for (size_t ix = 1; ix < row_heights.size(); ++ix)
{
//
//As we are commencing at one we know that we can safely compare
//the IX-1'th and the IX'th heights so that we can determine where
//the scrolled Y pixels value fits and thus determine the row
//clicked on.
//
printf("Row - Y = %g, hgt[%lu]=%d, hgt[%lu]=%d, popup = %d\n",
scrolled_pixels_y, ix - 1, row_heights[ix-1], ix, row_heights[ix], popup_y);
if (scrolled_pixels_y >= row_heights[ix - 1] &&
scrolled_pixels_y <= row_heights[ix])
{
//
//Now that we know that the clicked on row is IX we must
//decrement it by two given that the rows are zero based
//and the header columns are also part of the row data.
//
row = ix - 2;
break;
}
}
}
printf("Cell = %d%c\n", row + 1, col - 1 + 'A');
}
The problem is that the actual popup coordinates in popup_x
and popup_y
do not seem to match the cell that I click on. The values of popup_y
for the row can be larger than the sizes that I have calculated and thus returns invalid data. A similar problem exists for popup_x
.
Has anybody tried to do a similar thing ? I'm not fussed if it not a C or C++ example as I can read a little python and could convert the concepts.