Ticket #4206: StableWindowList.patch
File StableWindowList.patch, 21.3 KB (added by psprint, 4 years ago) |
---|
-
lib/widget.h
From 9e29fe1233844d6908277aff6354f0bfe395b1fa Mon Sep 17 00:00:00 2001 From: Sebastian Gniazdowski <sgniazdowski@gmail.com> Date: Thu, 18 Feb 2021 02:12:39 -0600 Subject: Stable, unchanged order of files in WindowList action. --- lib/widget.h | 1 + lib/widget/Makefile.am | 1 + lib/widget/dialog.c | 49 ++++-- lib/widget/dialog.h | 6 + lib/widget/order_kept_dialog.c | 297 +++++++++++++++++++++++++++++++++ lib/widget/order_kept_dialog.h | 50 ++++++ src/editor/editwidget.c | 56 ++++--- 7 files changed, 423 insertions(+), 37 deletions(-) create mode 100644 lib/widget/order_kept_dialog.c create mode 100644 lib/widget/order_kept_dialog.h diff --git a/lib/widget.h b/lib/widget.h index e3bb5cac2..2bbbd2cfb 100644
a b typedef struct WGroup WGroup; 21 21 #include "lib/widget/background.h" 22 22 #include "lib/widget/frame.h" 23 23 #include "lib/widget/dialog.h" 24 #include "lib/widget/order_kept_dialog.h" 24 25 #include "lib/widget/history.h" 25 26 #include "lib/widget/button.h" 26 27 #include "lib/widget/buttonbar.h" -
lib/widget/Makefile.am
diff --git a/lib/widget/Makefile.am b/lib/widget/Makefile.am index 90f023bbc..b7276c47b 100644
a b libmcwidget_la_SOURCES = \ 7 7 buttonbar.c buttonbar.h \ 8 8 check.c check.h \ 9 9 dialog.c dialog.h \ 10 order_kept_dialog.c order_kept_dialog.h \ 10 11 dialog-switch.c dialog-switch.h \ 11 12 frame.c frame.h \ 12 13 gauge.c gauge.h \ -
lib/widget/dialog.c
diff --git a/lib/widget/dialog.c b/lib/widget/dialog.c index b8a08f029..c400cd0c0 100644
a b dlg_create (gboolean modal, int y1, int x1, int lines, int cols, widget_pos_flag 373 373 gboolean compact, const int *colors, widget_cb_fn callback, 374 374 widget_mouse_cb_fn mouse_callback, const char *help_ctx, const char *title) 375 375 { 376 WDialog *new_d; 376 WDialog *new_h; 377 new_h = g_new0 (WDialog, 1); 378 if (new_h != NULL) 379 { 380 gboolean ret; 381 ret = dlg_init_object (new_h, modal, y1, x1, lines, cols, pos_flags, compact, colors, 382 callback, mouse_callback, help_ctx, title); 383 if (!ret) 384 { 385 g_free (new_h); 386 return NULL; 387 } 388 } 389 return new_h; 390 } 391 392 /* --------------------------------------------------------------------------------------------- */ 393 394 gboolean 395 dlg_init_object (WDialog * new_h, gboolean modal, int y1, int x1, int lines, int cols, 396 widget_pos_flags_t pos_flags, gboolean compact, const int *colors, 397 widget_cb_fn callback, widget_mouse_cb_fn mouse_callback, const char *help_ctx, 398 const char *title) 399 { 377 400 Widget *w; 378 401 WGroup *g; 379 402 380 new_d = g_new0 (WDialog, 1);381 w = WIDGET (new_d);382 g = GROUP (new_d); 403 w = WIDGET (new_h); 404 g = GROUP (new_h); 405 383 406 widget_adjust_position (pos_flags, &y1, &x1, &lines, &cols); 384 407 group_init (g, y1, x1, lines, cols, callback != NULL ? callback : dlg_default_callback, 385 408 mouse_callback != NULL ? mouse_callback : dlg_default_mouse_callback); … … dlg_create (gboolean modal, int y1, int x1, int lines, int cols, widget_pos_flag 397 420 398 421 w->get_colors = dlg_default_get_colors; 399 422 400 new_ d->colors = colors;401 new_ d->help_ctx = help_ctx;402 new_ d->compact = compact;403 new_ d->data = NULL;423 new_h->colors = colors; 424 new_h->help_ctx = help_ctx; 425 new_h->compact = compact; 426 new_h->data = NULL; 404 427 405 428 if (modal) 406 429 { 407 430 w->state |= WST_MODAL; 408 431 409 new_ d->bg = WIDGET (frame_new (0, 0, w->lines, w->cols, title, FALSE, new_d->compact));410 group_add_widget (g, new_ d->bg);411 frame_set_title (FRAME (new_ d->bg), title);432 new_h->bg = WIDGET (frame_new (0, 0, w->lines, w->cols, title, FALSE, new_h->compact)); 433 group_add_widget (g, new_h->bg); 434 frame_set_title (FRAME (new_h->bg), title); 412 435 } 413 436 414 437 /* unique name of event group for this dialog */ 415 new_ d->event_group = g_strdup_printf ("%s_%p", MCEVENT_GROUP_DIALOG, (void *) new_d);438 new_h->event_group = g_strdup_printf ("%s_%p", MCEVENT_GROUP_DIALOG, (void *) new_h); 416 439 417 return new_d;440 return TRUE; 418 441 } 419 442 420 443 /* --------------------------------------------------------------------------------------------- */ -
lib/widget/dialog.h
diff --git a/lib/widget/dialog.h b/lib/widget/dialog.h index 1d08b8e1a..ed90118b4 100644
a b WDialog *dlg_create (gboolean modal, int y1, int x1, int lines, int cols, 102 102 const int *colors, widget_cb_fn callback, widget_mouse_cb_fn mouse_callback, 103 103 const char *help_ctx, const char *title); 104 104 105 gboolean dlg_init_object (WDialog * new_h, gboolean modal, int y1, int x1, int lines, int cols, 106 widget_pos_flags_t pos_flags, gboolean compact, 107 const int *colors, widget_cb_fn callback, 108 widget_mouse_cb_fn mouse_callback, const char *help_ctx, 109 const char *title); 110 105 111 void dlg_set_default_colors (void); 106 112 107 113 void dlg_init (WDialog * h); -
new file lib/widget/order_kept_dialog.c
diff --git a/lib/widget/order_kept_dialog.c b/lib/widget/order_kept_dialog.c new file mode 100644 index 000000000..766caefbc
- + 1 /* 2 This dialog keeps a fixed order list of the widgets in its group (no Z-depth reordering). 3 4 Copyright (C) 2021 5 Free Software Foundation, Inc. 6 7 Written by: 8 Sebastian Gniazdowski <sgniazdowski@gmail.com>, 2021 9 10 This file is part of the Midnight Commander. 11 12 The Midnight Commander is free software: you can redistribute it 13 and/or modify it under the terms of the GNU General Public License as 14 published by the Free Software Foundation, either version 3 of the License, 15 or (at your option) any later version. 16 17 The Midnight Commander is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License for more details. 21 22 You should have received a copy of the GNU General Public License 23 along with this program. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 /** \file order_kept_dlg 27 * \brief This extension of Dialog is to have a stable, fixed list of files in MCEdit. 28 * \author Sebastian Gniazdowski 29 * \date 2021 30 * 31 * Before this class the files were fluctuating when they have been selected, as they 32 * were brought to the front of the Z axis, which has been moving them in the `widgets` 33 * list of the Group widget. 34 */ 35 36 #include <config.h> 37 38 #include "lib/global.h" 39 #include "lib/widget.h" 40 41 #include "src/editor/editwidget.h" 42 43 /*** global variables ****************************************************************************/ 44 45 /*** file scope macro definitions ****************************************************************/ 46 47 /*** file scope type declarations ****************************************************************/ 48 49 /*** file scope variables ************************************************************************/ 50 51 /*** file scope functions ************************************************************************/ 52 /* --------------------------------------------------------------------------------------------- */ 53 54 /* To search WEdit by file name */ 55 static gboolean 56 filename_equal_edit_widgets (gconstpointer a, gconstpointer b) 57 { 58 WEdit *wa = (WEdit *) a; 59 const char *fname_a = NULL, *fname_b = (const char *) b; 60 61 if (wa->filename_vpath != NULL) 62 fname_a = vfs_path_as_str (wa->filename_vpath); 63 64 return (g_strcmp0 (fname_a, fname_b) == 0); 65 } 66 67 /* --------------------------------------------------------------------------------------------- */ 68 /* Return a new string with old extension replaced by new extension */ 69 70 static char * 71 replace_suffix_with (const char *src_str, const char *old_suffix, const char *new_suffix) 72 { 73 char *new_str, *sufx_ptr; 74 new_str = g_strndup (src_str, strlen (src_str) + strlen (new_suffix)); 75 sufx_ptr = g_strrstr (new_str, old_suffix); 76 if (sufx_ptr != NULL) 77 { 78 g_stpcpy (sufx_ptr, new_suffix); 79 return new_str; 80 } 81 else 82 { 83 g_free (new_str); 84 return NULL; 85 } 86 } 87 88 /* --------------------------------------------------------------------------------------------- */ 89 /* Sorts widgets by clustering headers and sources adjacent */ 90 91 static gboolean 92 sort_edit_widgets (GPtrArray * array, order_kept_dialog_sort_flags_t sort_flags) 93 { 94 WEdit *we; 95 const char *fname; 96 char *fname_con_c = NULL, *fname_con_cpp = NULL; 97 guint con_idx = 0, idx; 98 gboolean ret = FALSE, ret_con; 99 100 const char *src_sufx, *con_sufx_1, *con_sufx_2; 101 102 /* No need to sort? */ 103 if ((sort_flags & (ORDER_DLG_GLUE_HEADERS_AND_SOURCE | 104 ORDER_DLG_GLUE_HEADERS_AND_SOURCE_REVERSE)) == 0) 105 return ret; 106 107 /* Iterate over array inserting headers before/after its sources */ 108 for (idx = 0; idx < array->len; idx++) 109 { 110 we = (WEdit *) g_ptr_array_index (array, idx); 111 112 if (we->filename_vpath == NULL) 113 continue; 114 fname = vfs_path_as_str (we->filename_vpath); 115 116 if (fname == NULL) 117 continue; 118 119 /* Is it a source? */ 120 if (g_str_has_suffix (fname, ".c") || g_str_has_suffix (fname, ".cpp")) 121 { 122 src_sufx = ".c"; 123 con_sufx_1 = ".h"; 124 con_sufx_2 = ".hpp"; 125 126 /* Is it a header? */ 127 } 128 else if (g_str_has_suffix (fname, ".h") || g_str_has_suffix (fname, ".hpp")) 129 { 130 src_sufx = ".h"; 131 con_sufx_1 = ".c"; 132 con_sufx_2 = ".cpp"; 133 } 134 135 /* Other kind -> ignore */ 136 else 137 continue; 138 139 /* Construct a C and C++ contrary file */ 140 fname_con_c = replace_suffix_with (fname, src_sufx, con_sufx_1); 141 fname_con_cpp = replace_suffix_with (fname, src_sufx, con_sufx_2); 142 143 /* Search for C extension contary file */ 144 ret_con = g_ptr_array_find_with_equal_func (array, fname_con_c, 145 filename_equal_edit_widgets, &con_idx); 146 147 /* If needed, retry with C++ extension file */ 148 if (!ret_con) 149 ret_con = g_ptr_array_find_with_equal_func (array, fname_con_cpp, 150 filename_equal_edit_widgets, &con_idx); 151 152 g_free (fname_con_c); 153 g_free (fname_con_cpp); 154 155 /* There's no contary file? */ 156 if (!ret_con || con_idx == idx || array->pdata[idx] == array->pdata[con_idx]) 157 continue; 158 159 ret = TRUE; 160 161 /* Move headers and their sources at adjacent positions */ 162 if (con_idx < idx && (sort_flags & ORDER_DLG_GLUE_HEADERS_AND_SOURCE) != 0) 163 { 164 gpointer file = g_ptr_array_steal_index (array, idx); 165 g_ptr_array_insert (array, (src_sufx[1] == 'h') ? con_idx : con_idx + 1, file); 166 } 167 else if (idx < con_idx && (sort_flags & ORDER_DLG_GLUE_HEADERS_AND_SOURCE) != 0) 168 { 169 gpointer con_file = g_ptr_array_steal_index (array, con_idx); 170 g_ptr_array_insert (array, (src_sufx[1] == 'h') ? idx + 1 : idx, con_file); 171 idx++; 172 } 173 else if (con_idx < idx && (sort_flags & ORDER_DLG_GLUE_HEADERS_AND_SOURCE_REVERSE) != 0) 174 { 175 gpointer file = g_ptr_array_steal_index (array, idx); 176 g_ptr_array_insert (array, (src_sufx[1] == 'h') ? con_idx + 1 : con_idx, file); 177 } 178 else if (idx < con_idx && (sort_flags & ORDER_DLG_GLUE_HEADERS_AND_SOURCE_REVERSE) != 0) 179 { 180 gpointer con_file = g_ptr_array_steal_index (array, con_idx); 181 g_ptr_array_insert (array, (src_sufx[1] == 'h') ? idx : idx + 1, con_file); 182 idx++; 183 } 184 }; 185 return ret; 186 } 187 188 /* --------------------------------------------------------------------------------------------- */ 189 /* Called after any action properly handled by front WDialog */ 190 191 static gboolean 192 order_dlg_update_widgets_array (WOrderKeptDialog * h) 193 { 194 GList *w; 195 gboolean ret = FALSE; 196 guint idx; 197 198 /* Initialize pointer array */ 199 if (h->ordered_widgets == NULL) 200 h->ordered_widgets = g_ptr_array_new (); 201 202 /* Copy newly opened files */ 203 for (w = GROUP (h)->widgets; w != NULL; w = g_list_next (w)) 204 { 205 if (edit_widget_is_editor (CONST_WIDGET (w->data)) && 206 !g_ptr_array_find (h->ordered_widgets, w->data, NULL)) 207 { 208 g_ptr_array_add (h->ordered_widgets, w->data); 209 ret = TRUE; 210 } 211 } 212 213 /* Pop closed files */ 214 for (idx = 0; idx < h->ordered_widgets->len; idx++) 215 if (g_list_find (GROUP (h)->widgets, g_ptr_array_index (h->ordered_widgets, idx)) == NULL) 216 g_ptr_array_remove_index (h->ordered_widgets, idx); 217 218 /* Sort if needed */ 219 if (ret) 220 order_dlg_sort_widgets (h); 221 222 return ret; 223 } 224 225 /* --------------------------------------------------------------------------------------------- */ 226 /*** public functions ****************************************************************************/ 227 /* --------------------------------------------------------------------------------------------- */ 228 229 WOrderKeptDialog * 230 order_dlg_create (gboolean modal, int y1, int x1, int lines, int cols, 231 widget_pos_flags_t pos_flags, gboolean compact, 232 const int *colors, widget_cb_fn callback, 233 widget_cb_fn base_callback, 234 widget_mouse_cb_fn mouse_callback, 235 const char *help_ctx, const char *title, order_kept_dialog_sort_flags_t sort) 236 { 237 WOrderKeptDialog *new_oh; 238 gboolean ret; 239 240 new_oh = g_new0 (WOrderKeptDialog, 1); 241 new_oh->sort_flags = sort; 242 ret = dlg_init_object (DIALOG (new_oh), modal, y1, x1, lines, cols, pos_flags, compact, colors, 243 callback, mouse_callback, help_ctx, title); 244 if (!ret) 245 { 246 g_free (new_oh); 247 return NULL; 248 } 249 250 new_oh->base_callback = base_callback; 251 return new_oh; 252 } 253 254 /* --------------------------------------------------------------------------------------------- */ 255 256 cb_ret_t 257 order_dlg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 258 { 259 WOrderKeptDialog *h = ORDER_DIALOG (w); 260 cb_ret_t ret = MSG_NOT_HANDLED; 261 262 switch (msg) 263 { 264 case MSG_INIT: 265 /* Initial copy of WEdit pointers kept in WGroup::widgets */ 266 order_dlg_update_widgets_array (h); 267 break; 268 case MSG_DESTROY: 269 /* Only a shallow release, as WEdit objects are owned by WGroup base object */ 270 g_ptr_array_free (h->ordered_widgets, TRUE); 271 h->ordered_widgets = NULL; 272 break; 273 default: 274 break; 275 } 276 277 if (ret != MSG_HANDLED) 278 { 279 ret = h->base_callback (w, sender, msg, parm, data); 280 if (ret == MSG_HANDLED) 281 /* Add and pop new or closed files */ 282 order_dlg_update_widgets_array (h); 283 } 284 285 return ret; 286 } 287 288 /* --------------------------------------------------------------------------------------------- */ 289 /* Sorts main widget list (adjacent headers/sources) */ 290 291 void 292 order_dlg_sort_widgets (WOrderKeptDialog * h) 293 { 294 sort_edit_widgets (h->ordered_widgets, h->sort_flags); 295 } 296 297 /* --------------------------------------------------------------------------------------------- */ -
new file lib/widget/order_kept_dialog.h
diff --git a/lib/widget/order_kept_dialog.h b/lib/widget/order_kept_dialog.h new file mode 100644 index 000000000..34bd8a56f
- + 1 #ifndef MC__TEMPLATE_H 2 #define MC__TEMPLATE_H 3 4 /*** typedefs(not structures) and defined constants **********************************************/ 5 6 #define ORDER_DIALOG(x) ((WOrderKeptDialog *)(x)) 7 #define CONST_ORDER_DIALOG(x) ((const WOrderKeptDialog *)(x)) 8 9 /*** enums ***************************************************************************************/ 10 11 /* Default is: (1<<0) | (1<<7) */ 12 typedef enum 13 { 14 ORDER_DLG_GLUE_HEADERS_AND_SOURCE = (1 << 0), 15 ORDER_DLG_GLUE_HEADERS_AND_SOURCE_REVERSE = (1 << 1), 16 ORDER_DLG_ALLOW_STANDARD_REORDERING = (1 << 2), 17 ORDER_DLG_SORT_REVERSE = (1 << 3), 18 ORDER_DLG_SORT_BACKUP_FILES_LAST = (1 << 5), 19 ORDER_DLG_DEFAULT_SORT = (1 << 7) 20 } order_kept_dialog_sort_flags_t; 21 22 /*** structures declarations (and typedefs of structures)*****************************************/ 23 24 typedef struct WOrderKeptDialog 25 { 26 WDialog base; 27 GPtrArray *ordered_widgets; 28 widget_cb_fn base_callback; 29 order_kept_dialog_sort_flags_t sort_flags; 30 } WOrderKeptDialog; 31 32 /*** global variables defined in .c file *********************************************************/ 33 34 /*** declarations of public functions ************************************************************/ 35 36 WOrderKeptDialog *order_dlg_create (gboolean modal, int y1, int x1, int lines, int cols, 37 widget_pos_flags_t pos_flags, gboolean compact, 38 const int *colors, widget_cb_fn callback, 39 widget_cb_fn base_callback, 40 widget_mouse_cb_fn mouse_callback, 41 const char *help_ctx, const char *title, 42 order_kept_dialog_sort_flags_t sort); 43 44 cb_ret_t order_dlg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 45 46 void order_dlg_sort_widgets (WOrderKeptDialog * h); 47 48 /*** inline functions ****************************************************************************/ 49 50 #endif /* MC__ORDER_DIALOG_H */ -
src/editor/editwidget.c
diff --git a/src/editor/editwidget.c b/src/editor/editwidget.c index 18ac00e66..863b898cf 100644
a b get_hotkey (int n) 295 295 static void 296 296 edit_window_list (const WDialog * h) 297 297 { 298 const WGroup *g = CONST_GROUP (h); 299 const size_t offset = 2; /* skip menu and buttonbar */ 300 const size_t dlg_num = g_list_length (g->widgets) - offset; 298 const WOrderKeptDialog *od = ORDER_DIALOG (h); 299 const size_t dlg_num = od->ordered_widgets->len; 301 300 int lines, cols; 302 301 Listbox *listbox; 303 GList *w;304 302 WEdit *selected; 305 int i = 0; 303 WListbox *lw; 304 uint idx; 306 305 307 306 lines = MIN ((size_t) (LINES * 2 / 3), dlg_num); 308 307 cols = COLS * 2 / 3; 309 308 310 309 listbox = create_listbox_window (lines, cols, _("Open files"), "[Open files]"); 311 310 312 for (w = g->widgets; w != NULL; w = g_list_next (w)) 313 if (edit_widget_is_editor (CONST_WIDGET (w->data))) 314 { 315 WEdit *e = (WEdit *) w->data; 316 char *fname; 311 /* Convenience variable. */ 312 lw = LISTBOX (listbox->list); 317 313 318 if (e->filename_vpath == NULL) 319 fname = g_strdup_printf ("%c [%s]", e->modified ? '*' : ' ', _("NoName")); 320 else 321 fname = 322 g_strdup_printf ("%c%s", e->modified ? '*' : ' ', 323 vfs_path_as_str (e->filename_vpath)); 314 /* Iterate over designated ptr array */ 315 for (idx = 0; idx < od->ordered_widgets->len; idx++) 316 { 317 WEdit *e = (WEdit *) g_ptr_array_index (od->ordered_widgets, idx); 318 char *fname; 324 319 325 listbox_add_item (listbox->list, LISTBOX_APPEND_AT_END, get_hotkey (i++), 326 str_term_trim (fname, WIDGET (listbox->list)->cols - 2), e, FALSE); 327 g_free (fname); 328 } 320 if (e->filename_vpath == NULL) 321 fname = g_strdup_printf ("%c [%s]", e->modified ? '*' : ' ', _("NoName")); 322 else 323 fname = 324 g_strdup_printf ("%c%s", e->modified ? '*' : ' ', 325 vfs_path_as_str (e->filename_vpath)); 329 326 330 selected = run_listbox_with_data (listbox, g->current->data); 327 listbox_add_item (lw, LISTBOX_APPEND_AT_END, get_hotkey (idx), 328 str_term_trim (fname, WIDGET (lw)->cols - 2), e, FALSE); 329 g_free (fname); 330 } 331 332 selected = run_listbox_with_data (listbox, GROUP (od)->current->data); 331 333 if (selected != NULL) 332 334 widget_select (WIDGET (selected)); 333 335 } … … gboolean 1206 1208 edit_files (const GList * files) 1207 1209 { 1208 1210 static gboolean made_directory = FALSE; 1211 WOrderKeptDialog *order_edit_dlg; 1209 1212 WDialog *edit_dlg; 1210 1213 WGroup *g; 1211 1214 WMenuBar *menubar; … … edit_files (const GList * files) 1231 1234 } 1232 1235 1233 1236 /* Create a new dialog and add it widgets to it */ 1234 edit_dlg = 1235 dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, NULL, edit_dialog_callback, 1236 edit_dialog_mouse_callback, "[Internal File Editor]", NULL); 1237 order_edit_dlg = 1238 order_dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, NULL, 1239 order_dlg_callback, edit_dialog_callback, 1240 edit_dialog_mouse_callback, "[Internal File Editor]", NULL, 1241 ORDER_DLG_GLUE_HEADERS_AND_SOURCE); 1242 1243 edit_dlg = DIALOG (order_edit_dlg); 1237 1244 wd = WIDGET (edit_dlg); 1245 1238 1246 widget_want_tab (wd, TRUE); 1239 1247 wd->keymap = editor_map; 1240 1248 wd->ext_keymap = editor_x_map;