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 }