This source file includes following definitions.
- chattr_is_modifiable
- chattr_fill_str
- fileattrtext_fill
- fileattrtext_callback
- fileattrtext_new
- chattr_draw_select
- chattr_toggle_select
- chattrboxes_draw_scrollbar
- chattrboxes_draw
- chattrboxes_rename
- checkboxes_save_state
- chattrboxes_down
- chattrboxes_page_down
- chattrboxes_end
- chattrboxes_up
- chattrboxes_page_up
- chattrboxes_home
- chattrboxes_execute_cmd
- chattrboxes_key
- chattrboxes_callback
- chattrboxes_handle_mouse_event
- chattrboxes_mouse_callback
- chattrboxes_new
- chattr_init
- chattr_dlg_create
- chattr_done
- try_chattr
- do_chattr
- chattr_apply_mask
- chattr_cmd
- chattr_get_as_str
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 #include <config.h>
33
34 #include <errno.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37
38 #include <ext2fs/ext2_fs.h>
39
40 #include "lib/global.h"
41
42 #include "lib/tty/tty.h"
43 #include "lib/tty/color.h"
44 #include "lib/skin.h"
45 #include "lib/vfs/vfs.h"
46 #include "lib/widget.h"
47 #include "lib/util.h"
48
49 #include "src/keymap.h"
50 #include "src/util.h"
51
52 #include "cmd.h"
53
54
55
56
57
58 #define B_MARKED B_USER
59 #define B_SETALL (B_USER + 1)
60 #define B_SETMRK (B_USER + 2)
61 #define B_CLRMRK (B_USER + 3)
62
63 #define BUTTONS 6
64
65 #define CHATTRBOXES(x) ((WChattrBoxes *) (x))
66
67
68
69 typedef struct WFileAttrText WFileAttrText;
70
71 struct WFileAttrText
72 {
73 Widget widget;
74
75 char *filename;
76 int filename_width;
77 char attrs[32 + 1];
78 };
79
80 typedef struct WChattrBoxes WChattrBoxes;
81
82 struct WChattrBoxes
83 {
84 WGroup base;
85
86 int pos;
87 int top;
88 };
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 static struct
135 {
136 unsigned long flags;
137 char attr;
138 const char *text;
139 gboolean selected;
140 gboolean state;
141 } check_attr[] = {
142 { EXT2_SECRM_FL, 's', N_ ("Secure deletion"), FALSE, FALSE },
143 { EXT2_UNRM_FL, 'u', N_ ("Undelete"), FALSE, FALSE },
144 { EXT2_SYNC_FL, 'S', N_ ("Synchronous updates"), FALSE, FALSE },
145 { EXT2_DIRSYNC_FL, 'D', N_ ("Synchronous directory updates"), FALSE, FALSE },
146 { EXT2_IMMUTABLE_FL, 'i', N_ ("Immutable"), FALSE, FALSE },
147 { EXT2_APPEND_FL, 'a', N_ ("Append only"), FALSE, FALSE },
148 { EXT2_NODUMP_FL, 'd', N_ ("No dump"), FALSE, FALSE },
149 { EXT2_NOATIME_FL, 'A', N_ ("No update atime"), FALSE, FALSE },
150 { EXT2_COMPR_FL, 'c', N_ ("Compress"), FALSE, FALSE },
151 #ifdef EXT2_COMPRBLK_FL
152
153
154 { EXT2_COMPRBLK_FL, 'B', N_ ("Compressed clusters"), FALSE, FALSE },
155 #endif
156 #ifdef EXT2_DIRTY_FL
157
158
159 { EXT2_DIRTY_FL, 'Z', N_ ("Compressed dirty file"), FALSE, FALSE },
160 #endif
161 #ifdef EXT2_NOCOMPR_FL
162
163
164 { EXT2_NOCOMPR_FL, 'X', N_ ("Compression raw access"), FALSE, FALSE },
165 #endif
166 #ifdef EXT4_ENCRYPT_FL
167 { EXT4_ENCRYPT_FL, 'E', N_ ("Encrypted inode"), FALSE, FALSE },
168 #endif
169 { EXT3_JOURNAL_DATA_FL, 'j', N_ ("Journaled data"), FALSE, FALSE },
170 { EXT2_INDEX_FL, 'I', N_ ("Indexed directory"), FALSE, FALSE },
171 { EXT2_NOTAIL_FL, 't', N_ ("No tail merging"), FALSE, FALSE },
172 { EXT2_TOPDIR_FL, 'T', N_ ("Top of directory hierarchies"), FALSE, FALSE },
173 { EXT4_EXTENTS_FL, 'e', N_ ("Inode uses extents"), FALSE, FALSE },
174 #ifdef EXT4_HUGE_FILE_FL
175
176
177 { EXT4_HUGE_FILE_FL, 'h', N_ ("Huge_file"), FALSE, FALSE },
178 #endif
179 { FS_NOCOW_FL, 'C', N_ ("No COW"), FALSE, FALSE },
180 #ifdef FS_DAX_FL
181
182
183 { FS_DAX_FL, 'x', N_ ("Direct access for files"), FALSE, FALSE },
184 #endif
185 #ifdef EXT4_CASEFOLD_FL
186
187
188 { EXT4_CASEFOLD_FL, 'F', N_ ("Casefolded file"), FALSE, FALSE },
189 #endif
190 #ifdef EXT4_INLINE_DATA_FL
191 { EXT4_INLINE_DATA_FL, 'N', N_ ("Inode has inline data"), FALSE, FALSE },
192 #endif
193 #ifdef EXT4_PROJINHERIT_FL
194
195
196
197 { EXT4_PROJINHERIT_FL, 'P', N_ ("Project hierarchy"), FALSE, FALSE },
198 #endif
199 #ifdef EXT4_VERITY_FL
200
201
202
203
204 { EXT4_VERITY_FL, 'V', N_ ("Verity protected inode"), FALSE, FALSE },
205 #endif
206 };
207
208
209 static const size_t check_attr_num = G_N_ELEMENTS (check_attr);
210
211
212 static int check_attr_mod[32];
213 static int check_attr_mod_num = 0;
214
215
216 static int check_attr_width = 0;
217
218 static struct
219 {
220 int ret_cmd;
221 button_flags_t flags;
222 const char *text;
223 Widget *button;
224 } chattr_but[BUTTONS] = {
225 { B_SETALL, NORMAL_BUTTON, N_ ("Set &all"), NULL },
226 { B_MARKED, NORMAL_BUTTON, N_ ("&Marked all"), NULL },
227 { B_SETMRK, NORMAL_BUTTON, N_ ("S&et marked"), NULL },
228 { B_CLRMRK, NORMAL_BUTTON, N_ ("C&lear marked"), NULL },
229 { B_ENTER, DEFPUSH_BUTTON, N_ ("&Set"), NULL },
230 { B_CANCEL, NORMAL_BUTTON, N_ ("&Cancel"), NULL },
231 };
232
233 static gboolean flags_changed;
234 static int current_file;
235 static gboolean ignore_all;
236
237 static unsigned long and_mask, or_mask, flags;
238
239 static WFileAttrText *file_attr;
240
241
242 static const int wx = 3;
243
244
245
246
247
248 static inline gboolean
249 chattr_is_modifiable (size_t i)
250 {
251 return ((check_attr[i].flags & EXT2_FL_USER_MODIFIABLE) != 0);
252 }
253
254
255
256 static void
257 chattr_fill_str (unsigned long attr, char *str)
258 {
259 size_t i;
260
261 for (i = 0; i < check_attr_num; i++)
262 str[i] = (attr & check_attr[i].flags) != 0 ? check_attr[i].attr : '-';
263
264 str[check_attr_num] = '\0';
265 }
266
267
268
269 static void
270 fileattrtext_fill (WFileAttrText *fat, unsigned long attr)
271 {
272 chattr_fill_str (attr, fat->attrs);
273 widget_draw (WIDGET (fat));
274 }
275
276
277
278 static cb_ret_t
279 fileattrtext_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
280 {
281 WFileAttrText *fat = (WFileAttrText *) w;
282
283 switch (msg)
284 {
285 case MSG_DRAW:
286 {
287 int color = COLOR_NORMAL;
288 size_t i;
289
290 tty_setcolor (color);
291
292 if (w->rect.cols > fat->filename_width)
293 {
294 widget_gotoyx (w, 0, (w->rect.cols - fat->filename_width) / 2);
295 tty_print_string (fat->filename);
296 }
297 else
298 {
299 widget_gotoyx (w, 0, 0);
300 tty_print_string (str_trunc (fat->filename, w->rect.cols));
301 }
302
303
304 widget_gotoyx (w, 1, (w->rect.cols - check_attr_num) / 2);
305 for (i = 0; i < check_attr_num; i++)
306 {
307
308 if (chattr_is_modifiable (i))
309 {
310 if (color == DISABLED_COLOR)
311 {
312 color = COLOR_NORMAL;
313 tty_setcolor (color);
314 }
315 }
316 else
317 {
318 if (color != DISABLED_COLOR)
319 {
320 color = DISABLED_COLOR;
321 tty_setcolor (color);
322 }
323 }
324
325 tty_print_char (fat->attrs[i]);
326 }
327 return MSG_HANDLED;
328 }
329
330 case MSG_RESIZE:
331 {
332 const WRect *wo = &CONST_WIDGET (w->owner)->rect;
333
334 widget_default_callback (w, sender, msg, parm, data);
335
336 if (fat->filename_width > wo->cols - wx * 2)
337 {
338 w->rect.x = wo->x + wx;
339 w->rect.cols = wo->cols - wx * 2;
340 }
341 return MSG_HANDLED;
342 }
343
344 case MSG_DESTROY:
345 g_free (fat->filename);
346 return MSG_HANDLED;
347
348 default:
349 return widget_default_callback (w, sender, msg, parm, data);
350 }
351 }
352
353
354
355 static WFileAttrText *
356 fileattrtext_new (int y, int x, const char *filename, unsigned long attr)
357 {
358 WRect r = { y, x, 2, 1 };
359 WFileAttrText *fat;
360 int width;
361
362 width = str_term_width1 (filename);
363 r.cols = MAX (width, (int) check_attr_num);
364
365 fat = g_new (WFileAttrText, 1);
366 widget_init (WIDGET (fat), &r, fileattrtext_callback, NULL);
367
368 fat->filename = g_strdup (filename);
369 fat->filename_width = width;
370 fileattrtext_fill (fat, attr);
371
372 return fat;
373 }
374
375
376
377 static void
378 chattr_draw_select (const Widget *w, gboolean selected)
379 {
380 widget_gotoyx (w, 0, -1);
381 tty_print_char (selected ? '*' : ' ');
382 widget_gotoyx (w, 0, 1);
383 }
384
385
386
387 static void
388 chattr_toggle_select (const WChattrBoxes *cb, int Id)
389 {
390 Widget *w;
391
392
393 w = WIDGET (g_list_nth_data (CONST_GROUP (cb)->widgets, Id - cb->top));
394
395 check_attr[Id].selected = !check_attr[Id].selected;
396
397 tty_setcolor (COLOR_NORMAL);
398 chattr_draw_select (w, check_attr[Id].selected);
399 }
400
401
402
403 static inline void
404 chattrboxes_draw_scrollbar (const WChattrBoxes *cb)
405 {
406 const Widget *w = CONST_WIDGET (cb);
407 int max_line;
408 int line;
409 int i;
410
411
412 widget_gotoyx (w, 0, w->rect.cols);
413 if (cb->top == 0)
414 tty_print_one_vline (TRUE);
415 else
416 tty_print_char ('^');
417
418 max_line = w->rect.lines - 1;
419
420
421 widget_gotoyx (w, max_line, w->rect.cols);
422 if (cb->top + w->rect.lines == check_attr_mod_num || w->rect.lines >= check_attr_mod_num)
423 tty_print_one_vline (TRUE);
424 else
425 tty_print_char ('v');
426
427
428 line = 1 + (cb->pos * (w->rect.lines - 2)) / check_attr_mod_num;
429
430 for (i = 1; i < max_line; i++)
431 {
432 widget_gotoyx (w, i, w->rect.cols);
433 if (i != line)
434 tty_print_one_vline (TRUE);
435 else
436 tty_print_char ('*');
437 }
438 }
439
440
441
442 static void
443 chattrboxes_draw (WChattrBoxes *cb)
444 {
445 Widget *w = WIDGET (cb);
446 int i;
447 GList *l;
448 const int *colors;
449
450 colors = widget_get_colors (w);
451 tty_setcolor (colors[DLG_COLOR_NORMAL]);
452 tty_fill_region (w->rect.y, w->rect.x - 1, w->rect.lines, w->rect.cols + 1, ' ');
453
454
455 group_default_callback (w, NULL, MSG_DRAW, 0, NULL);
456
457
458 tty_setcolor (colors[DLG_COLOR_NORMAL]);
459 if (!mc_global.tty.slow_terminal && check_attr_mod_num > w->rect.lines)
460 chattrboxes_draw_scrollbar (cb);
461
462
463 for (i = cb->top, l = GROUP (cb)->widgets; l != NULL; i++, l = g_list_next (l))
464 chattr_draw_select (WIDGET (l->data), check_attr[i].selected);
465 }
466
467
468
469 static void
470 chattrboxes_rename (WChattrBoxes *cb)
471 {
472 Widget *w = WIDGET (cb);
473 gboolean active;
474 int i;
475 GList *l;
476
477 active = widget_get_state (w, WST_ACTIVE);
478
479
480 if (active)
481 widget_set_state (w, WST_SUSPENDED, TRUE);
482
483 for (i = cb->top, l = GROUP (cb)->widgets; l != NULL; i++, l = g_list_next (l))
484 {
485 WCheck *c = CHECK (l->data);
486 int m = check_attr_mod[i];
487 char btext[BUF_SMALL];
488
489 g_snprintf (btext, sizeof (btext), "(%c) %s", check_attr[m].attr, check_attr[m].text);
490 check_set_text (c, btext);
491 c->state = check_attr[m].state;
492 }
493
494
495 if (active)
496 widget_set_state (w, WST_ACTIVE, TRUE);
497
498 widget_draw (w);
499 }
500
501
502
503 static void
504 checkboxes_save_state (const WChattrBoxes *cb)
505 {
506 int i;
507 GList *l;
508
509 for (i = cb->top, l = CONST_GROUP (cb)->widgets; l != NULL; i++, l = g_list_next (l))
510 {
511 int m = check_attr_mod[i];
512
513 check_attr[m].state = CHECK (l->data)->state;
514 }
515 }
516
517
518
519 static cb_ret_t
520 chattrboxes_down (WChattrBoxes *cb)
521 {
522 if (cb->pos == cb->top + WIDGET (cb)->rect.lines - 1)
523 {
524
525
526
527 if (cb->pos == check_attr_mod_num - 1)
528
529 return MSG_NOT_HANDLED;
530
531
532 checkboxes_save_state (cb);
533 cb->pos++;
534 cb->top++;
535 chattrboxes_rename (cb);
536 }
537 else
538 {
539 GList *l;
540
541
542 cb->pos++;
543 l = g_list_next (GROUP (cb)->current);
544 widget_select (WIDGET (l->data));
545 }
546
547 return MSG_HANDLED;
548 }
549
550
551
552 static cb_ret_t
553 chattrboxes_page_down (WChattrBoxes *cb)
554 {
555 WGroup *g = GROUP (cb);
556 GList *l;
557
558 if (cb->pos == check_attr_mod_num - 1)
559 {
560
561
562
563 l = g_list_last (g->widgets);
564 }
565 else
566 {
567 int i = WIDGET (cb)->rect.lines;
568
569 checkboxes_save_state (cb);
570
571 if (cb->top > check_attr_mod_num - 2 * i)
572 i = check_attr_mod_num - i - cb->top;
573 if (cb->top + i < 0)
574 i = -cb->top;
575 if (i == 0)
576 {
577 cb->pos = check_attr_mod_num - 1;
578 cb->top += i;
579 l = g_list_last (g->widgets);
580 }
581 else
582 {
583 cb->pos += i;
584 cb->top += i;
585 l = g_list_nth (g->widgets, cb->pos - cb->top);
586 }
587
588 chattrboxes_rename (cb);
589 }
590
591 widget_select (WIDGET (l->data));
592
593 return MSG_HANDLED;
594 }
595
596
597
598 static cb_ret_t
599 chattrboxes_end (WChattrBoxes *cb)
600 {
601 GList *l;
602
603 checkboxes_save_state (cb);
604 cb->pos = check_attr_mod_num - 1;
605 cb->top = cb->pos - WIDGET (cb)->rect.lines + 1;
606 l = g_list_last (GROUP (cb)->widgets);
607 chattrboxes_rename (cb);
608 widget_select (WIDGET (l->data));
609
610 return MSG_HANDLED;
611 }
612
613
614
615 static cb_ret_t
616 chattrboxes_up (WChattrBoxes *cb)
617 {
618 if (cb->pos == cb->top)
619 {
620
621
622
623 if (cb->top == 0)
624
625 return MSG_NOT_HANDLED;
626
627
628 checkboxes_save_state (cb);
629 cb->pos--;
630 cb->top--;
631 chattrboxes_rename (cb);
632 }
633 else
634 {
635 GList *l;
636
637
638 cb->pos--;
639 l = g_list_previous (GROUP (cb)->current);
640 widget_select (WIDGET (l->data));
641 }
642
643 return MSG_HANDLED;
644 }
645
646
647
648 static cb_ret_t
649 chattrboxes_page_up (WChattrBoxes *cb)
650 {
651 WGroup *g = GROUP (cb);
652 GList *l;
653
654 if (cb->pos == 0 && cb->top == 0)
655 {
656
657
658
659 l = g_list_first (g->widgets);
660 }
661 else
662 {
663 int i = WIDGET (cb)->rect.lines;
664
665 checkboxes_save_state (cb);
666
667 if (cb->top < i)
668 i = cb->top;
669 if (i == 0)
670 {
671 cb->pos = 0;
672 cb->top -= i;
673 l = g_list_first (g->widgets);
674 }
675 else
676 {
677 cb->pos -= i;
678 cb->top -= i;
679 l = g_list_nth (g->widgets, cb->pos - cb->top);
680 }
681
682 chattrboxes_rename (cb);
683 }
684
685 widget_select (WIDGET (l->data));
686
687 return MSG_HANDLED;
688 }
689
690
691
692 static cb_ret_t
693 chattrboxes_home (WChattrBoxes *cb)
694 {
695 GList *l;
696
697 checkboxes_save_state (cb);
698 cb->pos = 0;
699 cb->top = 0;
700 l = g_list_first (GROUP (cb)->widgets);
701 chattrboxes_rename (cb);
702 widget_select (WIDGET (l->data));
703
704 return MSG_HANDLED;
705 }
706
707
708
709 static cb_ret_t
710 chattrboxes_execute_cmd (WChattrBoxes *cb, long command)
711 {
712 switch (command)
713 {
714 case CK_Down:
715 return chattrboxes_down (cb);
716
717 case CK_PageDown:
718 return chattrboxes_page_down (cb);
719
720 case CK_Bottom:
721 return chattrboxes_end (cb);
722
723 case CK_Up:
724 return chattrboxes_up (cb);
725
726 case CK_PageUp:
727 return chattrboxes_page_up (cb);
728
729 case CK_Top:
730 return chattrboxes_home (cb);
731
732 case CK_Mark:
733 case CK_MarkAndDown:
734 {
735 chattr_toggle_select (cb, cb->pos);
736 if (command == CK_MarkAndDown)
737 chattrboxes_down (cb);
738
739 return MSG_HANDLED;
740 }
741
742 default:
743 return MSG_NOT_HANDLED;
744 }
745 }
746
747
748
749 static cb_ret_t
750 chattrboxes_key (WChattrBoxes *cb, int key)
751 {
752 long command;
753
754 command = widget_lookup_key (WIDGET (cb), key);
755 if (command == CK_IgnoreKey)
756 return MSG_NOT_HANDLED;
757 return chattrboxes_execute_cmd (cb, command);
758 }
759
760
761
762 static cb_ret_t
763 chattrboxes_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
764 {
765 WChattrBoxes *cb = CHATTRBOXES (w);
766 WGroup *g = GROUP (w);
767
768 switch (msg)
769 {
770 case MSG_DRAW:
771 chattrboxes_draw (cb);
772 return MSG_HANDLED;
773
774 case MSG_NOTIFY:
775 {
776
777 int i;
778
779 i = g_list_index (g->widgets, sender);
780 if (i >= 0)
781 {
782 int m;
783
784 i += cb->top;
785 m = check_attr_mod[i];
786 flags ^= check_attr[m].flags;
787 fileattrtext_fill (file_attr, flags);
788 chattr_toggle_select (cb, i);
789 flags_changed = TRUE;
790 return MSG_HANDLED;
791 }
792 }
793 return MSG_NOT_HANDLED;
794
795 case MSG_CHANGED_FOCUS:
796
797 if (widget_get_state (sender, WST_FOCUSED))
798 {
799 int i;
800
801 i = g_list_index (g->widgets, sender);
802 cb->pos = cb->top + i;
803 }
804 return MSG_HANDLED;
805
806 case MSG_KEY:
807 {
808 cb_ret_t ret;
809
810 ret = chattrboxes_key (cb, parm);
811 if (ret != MSG_HANDLED)
812 ret = group_default_callback (w, NULL, MSG_KEY, parm, NULL);
813
814 return ret;
815 }
816
817 case MSG_ACTION:
818 return chattrboxes_execute_cmd (cb, parm);
819
820 case MSG_DESTROY:
821
822 checkboxes_save_state (cb);
823 MC_FALLTHROUGH;
824
825 default:
826 return group_default_callback (w, sender, msg, parm, data);
827 }
828 }
829
830
831
832 static int
833 chattrboxes_handle_mouse_event (Widget *w, Gpm_Event *event)
834 {
835 int mou;
836
837 mou = mouse_handle_event (w, event);
838 if (mou == MOU_UNHANDLED)
839 mou = group_handle_mouse_event (w, event);
840
841 return mou;
842 }
843
844
845
846 static void
847 chattrboxes_mouse_callback (Widget *w, mouse_msg_t msg, mouse_event_t *event)
848 {
849 WChattrBoxes *cb = CHATTRBOXES (w);
850
851 (void) event;
852
853 switch (msg)
854 {
855 case MSG_MOUSE_SCROLL_UP:
856 chattrboxes_up (cb);
857 break;
858
859 case MSG_MOUSE_SCROLL_DOWN:
860 chattrboxes_down (cb);
861 break;
862
863 default:
864
865 event->result.abort = TRUE;
866 break;
867 }
868 }
869
870
871
872 static WChattrBoxes *
873 chattrboxes_new (const WRect *r)
874 {
875 WChattrBoxes *cb;
876 Widget *w;
877 WGroup *cbg;
878 int i;
879
880 cb = g_new0 (WChattrBoxes, 1);
881 w = WIDGET (cb);
882 cbg = GROUP (cb);
883 group_init (cbg, r, chattrboxes_callback, chattrboxes_mouse_callback);
884 w->options |= WOP_SELECTABLE | WOP_WANT_CURSOR;
885 w->mouse_handler = chattrboxes_handle_mouse_event;
886 w->keymap = chattr_map;
887
888
889 for (i = 0; i < r->lines; i++)
890 {
891 int m = check_attr_mod[i];
892 WCheck *check;
893
894 check = check_new (i, 0, check_attr[m].state, NULL);
895 group_add_widget (cbg, check);
896 }
897
898 chattrboxes_rename (cb);
899
900
901 cbg->current = cbg->widgets;
902
903 return cb;
904 }
905
906
907
908 static void
909 chattr_init (void)
910 {
911 static gboolean i18n = FALSE;
912 size_t i;
913
914 for (i = 0; i < check_attr_num; i++)
915 check_attr[i].selected = FALSE;
916
917 if (i18n)
918 return;
919
920 i18n = TRUE;
921
922 for (i = 0; i < check_attr_num; i++)
923 if (chattr_is_modifiable (i))
924 {
925 int width;
926
927 #ifdef ENABLE_NLS
928 check_attr[i].text = _ (check_attr[i].text);
929 #endif
930
931 check_attr_mod[check_attr_mod_num++] = i;
932
933 width = 4 + str_term_width1 (check_attr[i].text);
934 check_attr_width = MAX (check_attr_width, width);
935 }
936
937 check_attr_width += 1 + 3 + 1;
938
939 #ifdef ENABLE_NLS
940 for (i = 0; i < BUTTONS; i++)
941 chattr_but[i].text = _ (chattr_but[i].text);
942 #endif
943 }
944
945
946
947 static WDialog *
948 chattr_dlg_create (WPanel *panel, const char *fname, unsigned long attr)
949 {
950 Widget *mw = WIDGET (WIDGET (panel)->owner);
951 gboolean single_set;
952 WDialog *ch_dlg;
953 int lines, cols;
954 int checkboxes_lines = check_attr_mod_num;
955 size_t i;
956 int y;
957 Widget *dw;
958 WGroup *dg;
959 WChattrBoxes *cb;
960 const int cb_scrollbar_width = 1;
961 WRect r;
962
963
964 for (i = 0; i < check_attr_num; i++)
965 check_attr[i].state = chattr_is_modifiable (i) && (attr & check_attr[i].flags) != 0;
966
967 cols = check_attr_width + cb_scrollbar_width;
968
969 single_set = (panel->marked < 2);
970
971 lines = 5 + checkboxes_lines + 4;
972 if (!single_set)
973 lines += 3;
974
975 if (lines >= mw->rect.lines - 2)
976 {
977 int dl;
978
979 dl = lines - (mw->rect.lines - 2);
980 lines -= dl;
981 checkboxes_lines -= dl;
982 }
983
984 ch_dlg = dlg_create (TRUE, 0, 0, lines, cols + wx * 2, WPOS_CENTER, FALSE, dialog_colors,
985 dlg_default_callback, NULL, "[Chattr]", _ ("Chattr command"));
986 dg = GROUP (ch_dlg);
987 dw = WIDGET (ch_dlg);
988
989 y = 2;
990 file_attr = fileattrtext_new (y, wx, fname, attr);
991 group_add_widget_autopos (dg, file_attr, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, NULL);
992 y += WIDGET (file_attr)->rect.lines;
993 group_add_widget (dg, hline_new (y++, -1, -1));
994
995 if (cols < WIDGET (file_attr)->rect.cols)
996 {
997 r = dw->rect;
998 cols = WIDGET (file_attr)->rect.cols;
999 cols = MIN (cols, mw->rect.cols - wx * 2);
1000 r.cols = cols + wx * 2;
1001 r.lines = lines;
1002 widget_set_size_rect (dw, &r);
1003 }
1004
1005 checkboxes_lines = MIN (check_attr_mod_num, checkboxes_lines);
1006 rect_init (&r, y++, wx, checkboxes_lines > 0 ? checkboxes_lines : 1, cols);
1007 cb = chattrboxes_new (&r);
1008 group_add_widget_autopos (dg, cb, WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
1009
1010 y += checkboxes_lines - 1;
1011 cols = 0;
1012
1013 for (i = single_set ? (BUTTONS - 2) : 0; i < BUTTONS; i++)
1014 {
1015 if (i == 0 || i == BUTTONS - 2)
1016 group_add_widget (dg, hline_new (y++, -1, -1));
1017
1018 chattr_but[i].button = WIDGET (button_new (y, 1, chattr_but[i].ret_cmd, chattr_but[i].flags,
1019 chattr_but[i].text, NULL));
1020 group_add_widget (dg, chattr_but[i].button);
1021
1022 const int bw0 = button_get_width (BUTTON (chattr_but[i].button));
1023
1024 i++;
1025 chattr_but[i].button = WIDGET (button_new (y++, 1, chattr_but[i].ret_cmd,
1026 chattr_but[i].flags, chattr_but[i].text, NULL));
1027 group_add_widget (dg, chattr_but[i].button);
1028
1029 const int bw1 = button_get_width (BUTTON (chattr_but[i].button));
1030
1031
1032 cols = MAX (cols, bw0 + 1 + bw1);
1033 }
1034
1035
1036 cols += 6;
1037 if (cols > dw->rect.cols)
1038 {
1039 r = dw->rect;
1040 r.lines = lines;
1041 r.cols = cols;
1042 widget_set_size_rect (dw, &r);
1043 }
1044
1045
1046 const int mid = dw->rect.x + dw->rect.cols / 2 + 1;
1047
1048
1049 for (i = single_set ? (BUTTONS - 2) : 0; i < BUTTONS; i++)
1050 {
1051 Widget *b;
1052
1053 b = chattr_but[i].button;
1054 r = b->rect;
1055 r.x = mid - r.cols;
1056 widget_set_size_rect (b, &r);
1057 i++;
1058 b = chattr_but[i].button;
1059 r = b->rect;
1060 r.x = mid + 1;
1061 widget_set_size_rect (b, &r);
1062 }
1063
1064 widget_select (WIDGET (cb));
1065
1066 return ch_dlg;
1067 }
1068
1069
1070
1071 static void
1072 chattr_done (gboolean need_update)
1073 {
1074 if (need_update)
1075 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
1076 repaint_screen ();
1077 }
1078
1079
1080
1081 static gboolean
1082 try_chattr (const vfs_path_t *p, unsigned long m)
1083 {
1084 const char *fname = NULL;
1085
1086 while (mc_fsetflags (p, m) == -1 && !ignore_all)
1087 {
1088 int my_errno = errno;
1089
1090 if (fname == NULL)
1091 fname = x_basename (vfs_path_as_str (p));
1092
1093 errno = my_errno;
1094
1095 switch (file_error (NULL, TRUE, _ ("Cannot chattr\n%sn%s"), fname))
1096 {
1097 case FILE_IGNORE:
1098
1099 return TRUE;
1100
1101 case FILE_IGNORE_ALL:
1102 ignore_all = TRUE;
1103
1104 return TRUE;
1105
1106 case FILE_RETRY:
1107
1108 break;
1109
1110 case FILE_ABORT:
1111 default:
1112
1113 return FALSE;
1114 }
1115 }
1116
1117 return TRUE;
1118 }
1119
1120
1121
1122 static gboolean
1123 do_chattr (WPanel *panel, const vfs_path_t *p, unsigned long m)
1124 {
1125 gboolean ret;
1126
1127 m &= and_mask;
1128 m |= or_mask;
1129
1130 ret = try_chattr (p, m);
1131
1132 do_file_mark (panel, current_file, 0);
1133
1134 return ret;
1135 }
1136
1137
1138
1139 static void
1140 chattr_apply_mask (WPanel *panel, vfs_path_t *vpath, unsigned long m)
1141 {
1142 gboolean ok;
1143
1144 if (!do_chattr (panel, vpath, m))
1145 return;
1146
1147 do
1148 {
1149 const GString *fname;
1150
1151 fname = panel_find_marked_file (panel, ¤t_file);
1152 vpath = vfs_path_from_str (fname->str);
1153 ok = (mc_fgetflags (vpath, &m) == 0);
1154
1155 if (!ok)
1156 {
1157
1158
1159 do_file_mark (panel, current_file, 0);
1160
1161
1162 ok = TRUE;
1163 }
1164 else
1165 {
1166 flags = m;
1167 ok = do_chattr (panel, vpath, m);
1168 vfs_path_free (vpath, TRUE);
1169 }
1170 }
1171 while (ok && panel->marked != 0);
1172 }
1173
1174
1175
1176
1177
1178 void
1179 chattr_cmd (WPanel *panel)
1180 {
1181 gboolean need_update = FALSE;
1182 gboolean end_chattr = FALSE;
1183
1184 chattr_init ();
1185
1186 current_file = 0;
1187 ignore_all = FALSE;
1188
1189 do
1190 {
1191 vfs_path_t *vpath;
1192 WDialog *ch_dlg;
1193 const GString *fname;
1194 size_t i;
1195 int result;
1196
1197 do_refresh ();
1198
1199 need_update = FALSE;
1200 end_chattr = FALSE;
1201
1202 fname = panel_get_marked_file (panel, ¤t_file);
1203 if (fname == NULL)
1204 break;
1205
1206 vpath = vfs_path_from_str (fname->str);
1207
1208 if (mc_fgetflags (vpath, &flags) != 0)
1209 {
1210 file_error_message (_ ("Cannot get ext2 attributes of\n%s"), fname->str);
1211 vfs_path_free (vpath, TRUE);
1212 break;
1213 }
1214
1215 flags_changed = FALSE;
1216
1217 ch_dlg = chattr_dlg_create (panel, fname->str, flags);
1218 result = dlg_run (ch_dlg);
1219 widget_destroy (WIDGET (ch_dlg));
1220
1221 switch (result)
1222 {
1223 case B_CANCEL:
1224 end_chattr = TRUE;
1225 break;
1226
1227 case B_ENTER:
1228 if (flags_changed)
1229 {
1230 if (panel->marked <= 1)
1231 {
1232
1233 if (mc_fsetflags (vpath, flags) == -1 && !ignore_all)
1234 file_error_message (_ ("Cannot chattr\n%s"), fname->str);
1235 end_chattr = TRUE;
1236 }
1237 else if (!try_chattr (vpath, flags))
1238 {
1239
1240 result = B_CANCEL;
1241 end_chattr = TRUE;
1242 }
1243 }
1244
1245 need_update = TRUE;
1246 break;
1247
1248 case B_SETALL:
1249 case B_MARKED:
1250 or_mask = 0;
1251 and_mask = ~0;
1252
1253 for (i = 0; i < check_attr_num; i++)
1254 if (chattr_is_modifiable (i) && (check_attr[i].selected || result == B_SETALL))
1255 {
1256 if (check_attr[i].state)
1257 or_mask |= check_attr[i].flags;
1258 else
1259 and_mask &= ~check_attr[i].flags;
1260 }
1261
1262 chattr_apply_mask (panel, vpath, flags);
1263 need_update = TRUE;
1264 end_chattr = TRUE;
1265 break;
1266
1267 case B_SETMRK:
1268 or_mask = 0;
1269 and_mask = ~0;
1270
1271 for (i = 0; i < check_attr_num; i++)
1272 if (chattr_is_modifiable (i) && check_attr[i].selected)
1273 or_mask |= check_attr[i].flags;
1274
1275 chattr_apply_mask (panel, vpath, flags);
1276 need_update = TRUE;
1277 end_chattr = TRUE;
1278 break;
1279
1280 case B_CLRMRK:
1281 or_mask = 0;
1282 and_mask = ~0;
1283
1284 for (i = 0; i < check_attr_num; i++)
1285 if (chattr_is_modifiable (i) && check_attr[i].selected)
1286 and_mask &= ~check_attr[i].flags;
1287
1288 chattr_apply_mask (panel, vpath, flags);
1289 need_update = TRUE;
1290 end_chattr = TRUE;
1291 break;
1292
1293 default:
1294 break;
1295 }
1296
1297 if (panel->marked != 0 && result != B_CANCEL)
1298 {
1299 do_file_mark (panel, current_file, 0);
1300 need_update = TRUE;
1301 }
1302
1303 vfs_path_free (vpath, TRUE);
1304 }
1305 while (panel->marked != 0 && !end_chattr);
1306
1307 chattr_done (need_update);
1308 }
1309
1310
1311
1312 const char *
1313 chattr_get_as_str (unsigned long attr)
1314 {
1315 static char str[32 + 1];
1316
1317 chattr_fill_str (attr, str);
1318
1319 return str;
1320 }
1321
1322