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