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.

0

There are 0 answers