File indexing completed on 2024-04-21 05:47:02

0001 /*****************************************************************************
0002  *   Copyright 2003 - 2010 Craig Drummond <craig.p.drummond@gmail.com>       *
0003  *   Copyright 2013 - 2015 Yichao Yu <yyc1992@gmail.com>                     *
0004  *                                                                           *
0005  *   This program is free software; you can redistribute it and/or modify    *
0006  *   it under the terms of the GNU Lesser General Public License as          *
0007  *   published by the Free Software Foundation; either version 2.1 of the    *
0008  *   License, or (at your option) version 3, or any later version accepted   *
0009  *   by the membership of KDE e.V. (or its successor approved by the         *
0010  *   membership of KDE e.V.), which shall act as a proxy defined in          *
0011  *   Section 6 of version 3 of the license.                                  *
0012  *                                                                           *
0013  *   This program is distributed in the hope that it will be useful,         *
0014  *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
0015  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *
0016  *   Lesser General Public License for more details.                         *
0017  *                                                                           *
0018  *   You should have received a copy of the GNU Lesser General Public        *
0019  *   License along with this library. If not,                                *
0020  *   see <http://www.gnu.org/licenses/>.                                     *
0021  *****************************************************************************/
0022 
0023 #include "treeview.h"
0024 
0025 #include <qtcurve-utils/gtkprops.h>
0026 #include <qtcurve-cairo/utils.h>
0027 
0028 namespace QtCurve {
0029 namespace TreeView {
0030 
0031 typedef struct {
0032     GtkTreePath *path;
0033     GtkTreeViewColumn *column;
0034     bool fullWidth;
0035 } QtCTreeView;
0036 
0037 static GHashTable *table = nullptr;
0038 
0039 static QtCTreeView*
0040 lookupHash(void *hash, bool create)
0041 {
0042     QtCTreeView *rv = nullptr;
0043 
0044     if (!table)
0045         table=g_hash_table_new(g_direct_hash, g_direct_equal);
0046 
0047     rv = (QtCTreeView*)g_hash_table_lookup(table, hash);
0048 
0049     if (!rv && create) {
0050         rv = qtcNew(QtCTreeView);
0051         rv->path=nullptr;
0052         rv->column=nullptr;
0053         rv->fullWidth=false;
0054         g_hash_table_insert(table, hash, rv);
0055         rv = (QtCTreeView*)g_hash_table_lookup(table, hash);
0056     }
0057 
0058     return rv;
0059 }
0060 
0061 static void
0062 removeFromHash(void *hash)
0063 {
0064     if (table) {
0065         QtCTreeView *tv = lookupHash(hash, false);
0066         if (tv) {
0067             if(tv->path)
0068                 gtk_tree_path_free(tv->path);
0069             g_hash_table_remove(table, hash);
0070         }
0071     }
0072 }
0073 
0074 static void
0075 cleanup(GtkWidget *widget)
0076 {
0077     GtkWidgetProps props(widget);
0078     if (widget && props->treeViewHacked) {
0079         removeFromHash(widget);
0080         props->treeViewDestroy.disconn();
0081         props->treeViewUnrealize.disconn();
0082         props->treeViewStyleSet.disconn();
0083         props->treeViewMotion.disconn();
0084         props->treeViewLeave.disconn();
0085         props->treeViewHacked = false;
0086     }
0087 }
0088 
0089 static gboolean
0090 styleSet(GtkWidget *widget, GtkStyle*, void*)
0091 {
0092     cleanup(widget);
0093     return false;
0094 }
0095 
0096 static gboolean
0097 destroy(GtkWidget *widget, GdkEvent*, void*)
0098 {
0099     cleanup(widget);
0100     return false;
0101 }
0102 
0103 static bool
0104 samePath(GtkTreePath *a, GtkTreePath *b)
0105 {
0106     return a ? (b && !gtk_tree_path_compare(a, b)) : !b;
0107 }
0108 
0109 static void
0110 updatePosition(GtkWidget *widget, int x, int y)
0111 {
0112     if (GTK_IS_TREE_VIEW(widget)) {
0113         QtCTreeView *tv = lookupHash(widget, false);
0114         if (tv) {
0115             GtkTreeView *treeView = GTK_TREE_VIEW(widget);
0116             GtkTreePath *path = nullptr;
0117             GtkTreeViewColumn *column = nullptr;
0118 
0119             gtk_tree_view_get_path_at_pos(treeView, x, y, &path,
0120                                           &column, nullptr, nullptr);
0121 
0122             if (!samePath(tv->path, path)) {
0123                 // prepare update area
0124                 // get old rectangle
0125                 QtcRect oldRect = {0, 0, -1, -1 };
0126                 QtcRect newRect = {0, 0, -1, -1 };
0127                 QtcRect updateRect;
0128                 QtcRect alloc = Widget::getAllocation(widget);
0129 
0130                 if (tv->path && tv->column) {
0131                     gtk_tree_view_get_background_area(
0132                         treeView, tv->path, tv->column,
0133                         (GdkRectangle*)&oldRect);
0134                 }
0135                 if (tv->fullWidth) {
0136                     oldRect.x = 0;
0137                     oldRect.width = alloc.width;
0138                 }
0139 
0140                 // get new rectangle and update position
0141                 if (path && column) {
0142                     gtk_tree_view_get_background_area(
0143                         treeView, path, column, (GdkRectangle*)&newRect);
0144                 }
0145                 if (path && column && tv->fullWidth) {
0146                     newRect.x = 0;
0147                     newRect.width = alloc.width;
0148                 }
0149 
0150                 // take the union of both rectangles
0151                 if (oldRect.width > 0 && oldRect.height > 0) {
0152                     if (newRect.width > 0 && newRect.height > 0) {
0153                         Rect::union_(&oldRect, &newRect, &updateRect);
0154                     } else {
0155                         updateRect = oldRect;
0156                     }
0157                 } else {
0158                     updateRect = newRect;
0159                 }
0160 
0161                 // store new cell info
0162                 if (tv->path)
0163                     gtk_tree_path_free(tv->path);
0164                 tv->path = path ? gtk_tree_path_copy(path) : nullptr;
0165                 tv->column = column;
0166 
0167                 // convert to widget coordinates and schedule redraw
0168                 gtk_tree_view_convert_bin_window_to_widget_coords(
0169                     treeView, updateRect.x, updateRect.y,
0170                     &updateRect.x, &updateRect.y);
0171                 gtk_widget_queue_draw_area(
0172                     widget, updateRect.x, updateRect.y,
0173                     updateRect.width, updateRect.height);
0174             }
0175 
0176             if (path) {
0177                 gtk_tree_path_free(path);
0178             }
0179         }
0180     }
0181 }
0182 
0183 static gboolean
0184 motion(GtkWidget *widget, GdkEventMotion *event, void*)
0185 {
0186     if (event && event->window && GTK_IS_TREE_VIEW(widget) &&
0187         gtk_tree_view_get_bin_window(GTK_TREE_VIEW(widget)) == event->window) {
0188         updatePosition(widget, event->x, event->y);
0189     }
0190     return false;
0191 }
0192 
0193 static gboolean
0194 leave(GtkWidget *widget, GdkEventMotion*, void*)
0195 {
0196     if (GTK_IS_TREE_VIEW(widget)) {
0197         QtCTreeView *tv = lookupHash(widget, false);
0198         if (tv) {
0199             GtkTreeView *treeView = GTK_TREE_VIEW(widget);
0200             QtcRect rect = {0, 0, -1, -1 };
0201             QtcRect alloc = Widget::getAllocation(widget);
0202 
0203             if (tv->path && tv->column) {
0204                 gtk_tree_view_get_background_area(
0205                     treeView, tv->path, tv->column, (GdkRectangle*)&rect);
0206             }
0207             if (tv->fullWidth) {
0208                 rect.x = 0;
0209                 rect.width = alloc.width;
0210             }
0211             if (tv->path) {
0212                 gtk_tree_path_free(tv->path);
0213             }
0214             tv->path = nullptr;
0215             tv->column = nullptr;
0216 
0217             gtk_tree_view_convert_bin_window_to_widget_coords(
0218                 treeView, rect.x, rect.y, &rect.x, &rect.y);
0219             gtk_widget_queue_draw_area(
0220                 widget, rect.x, rect.y, rect.width, rect.height);
0221         }
0222     }
0223     return false;
0224 }
0225 
0226 void
0227 getCell(GtkTreeView *treeView, GtkTreePath **path, GtkTreeViewColumn **column,
0228         int x, int y, int width, int height)
0229 {
0230     const GdkPoint points[4] = {{x + 1, y + 1}, {x + 1, y + height - 1},
0231                                 {x + width - 1, y + 1},
0232                                 {x + width, y + height - 1}};
0233     for (int pos = 0;pos < 4 && !(*path);pos++) {
0234         gtk_tree_view_get_path_at_pos(treeView, points[pos].x, points[pos].y,
0235                                       path, column, nullptr, nullptr);
0236     }
0237 }
0238 
0239 void
0240 setup(GtkWidget *widget)
0241 {
0242     GtkWidgetProps props(widget);
0243     if (widget && !props->treeViewHacked) {
0244         QtCTreeView *tv = lookupHash(widget, true);
0245         GtkTreeView *treeView = GTK_TREE_VIEW(widget);
0246         GtkWidget *parent = gtk_widget_get_parent(widget);
0247 
0248         if (tv) {
0249             props->treeViewHacked = true;
0250             int x, y;
0251 #if GTK_CHECK_VERSION(2, 90, 0) /* Gtk3:TODO !!! */
0252             tv->fullWidth = true;
0253 #else
0254             gtk_widget_style_get(widget, "row_ending_details",
0255                                  &tv->fullWidth, nullptr);
0256 #endif
0257             gdk_window_get_pointer(gtk_widget_get_window(widget),
0258                                    &x, &y, nullptr);
0259             gtk_tree_view_convert_widget_to_bin_window_coords(treeView, x, y,
0260                                                               &x, &y);
0261             updatePosition(widget, x, y);
0262             props->treeViewDestroy.conn("destroy-event", destroy);
0263             props->treeViewUnrealize.conn("unrealize", destroy);
0264             props->treeViewStyleSet.conn("style-set", styleSet);
0265             props->treeViewMotion.conn("motion-notify-event", motion);
0266             props->treeViewLeave.conn("leave-notify-event", leave);
0267         }
0268 
0269         if (!gtk_tree_view_get_show_expanders(treeView))
0270             gtk_tree_view_set_show_expanders(treeView, true);
0271         if (gtk_tree_view_get_enable_tree_lines(treeView))
0272             gtk_tree_view_set_enable_tree_lines(treeView, false);
0273 
0274         if (GTK_IS_SCROLLED_WINDOW(parent) &&
0275             gtk_scrolled_window_get_shadow_type(GTK_SCROLLED_WINDOW(parent)) !=
0276             GTK_SHADOW_IN) {
0277             gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(parent),
0278                                                 GTK_SHADOW_IN);
0279         }
0280     }
0281 }
0282 
0283 bool
0284 isCellHovered(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *column)
0285 {
0286     QtCTreeView *tv = lookupHash(widget, false);
0287     return (tv && (tv->fullWidth || tv->column == column) &&
0288             samePath(path, tv->path));
0289 }
0290 
0291 bool
0292 cellIsLeftOfExpanderColumn(GtkTreeView *treeView, GtkTreeViewColumn *column)
0293 {
0294     // check expander column
0295     GtkTreeViewColumn *expanderColumn =
0296         gtk_tree_view_get_expander_column(treeView);
0297 
0298     if (!expanderColumn || column == expanderColumn) {
0299         return false;
0300     } else {
0301         bool found = false;
0302         bool isLeft = false;
0303         // get all columns
0304         GList *columns = gtk_tree_view_get_columns(treeView);
0305         for (GList *child = columns;child;child = g_list_next(child)) {
0306             if (!GTK_IS_TREE_VIEW_COLUMN(child->data)) {
0307                 continue;
0308             }
0309             GtkTreeViewColumn *childCol = GTK_TREE_VIEW_COLUMN(child->data);
0310             if (childCol == expanderColumn) {
0311                 if (found) {
0312                     isLeft = true;
0313                 }
0314             } else if (found) {
0315                 break;
0316             } else if (column == childCol) {
0317                 found = true;
0318             }
0319         }
0320         if (columns) {
0321             g_list_free(columns);
0322         }
0323         return isLeft;
0324     }
0325 }
0326 
0327 }
0328 }