Subversion Repositories Filer-Free

Rev

Rev 51 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
31 gdshaw@RISCPKG.ORG 1
// This file is part of the free Filer module for RISC OS.
2
// Copyright  2007 Graham Shaw.
3
// Redistribution and modification are permitted under the terms of the
4
// GNU General Public License (version 2 or any later version).
5
 
6
#include <cstring>
7
#include <cstdio>
8
 
9
#include "oslib/fileraction.h"
10
#include "oslib/osfscontrol.h"
11
 
12
#include "filer_options.h"
13
#include "filer_window.h"
14
#include "filer_menu.h"
15
 
16
namespace {
17
 
18
/** The maximum length of a leafname. */
19
const size_t max_name_length=256;
20
 
21
}; /* anonymous namespace */
22
 
23
filer_menu::filer_menu(application* app):
24
        menu(app,"Filer",9),
25
        _owner(0),
26
        _object(0),
27
        _display_menu(app,"Display",8),
28
        _rename_menu(app,"Rename",1),
29
        _access_menu(app,"Access",5),
30
        _find_menu(app,"Find",1),
31
        _filetype_menu(app,"Set type",1),
32
        _selection_menu(app,"File",11),
33
        _options_menu(app,"Options",6),
38 gdshaw@RISCPKG.ORG 34
        _new_dir_dbox(app),
49 gdshaw@RISCPKG.ORG 35
        _copy_dbox(app),
52 gdshaw@RISCPKG.ORG 36
        _access_dbox(app),
41 gdshaw@RISCPKG.ORG 37
        _info_dbox(app),
31 gdshaw@RISCPKG.ORG 38
        _rename_leafname(0),
39
        _find_pattern(0),
40
        _filetype_field(0),
41
        _selection_text(new char[12+max_name_length]),
42
        _layout_count(3),
43
        _layout_methods(new layout_method*[_layout_count]),
44
        _sort_count(4),
45
        _sort_methods(new sort_method*[_sort_count])
46
{
47
        _layout_methods[0]=&filer_options::layout_large();
48
        _layout_methods[1]=&filer_options::layout_small();
49
        _layout_methods[2]=&filer_options::layout_full();
50
 
51
        _sort_methods[0]=&filer_options::sort_by_name();
52
        _sort_methods[1]=&filer_options::sort_by_type();
53
        _sort_methods[2]=&filer_options::sort_by_size();
54
        _sort_methods[3]=&filer_options::sort_by_date();
55
 
56
        _display_menu[0].text("Large icons");
57
        _display_menu[1].text("Small icons");
58
        _display_menu[2].text("Full info");
59
        _display_menu[3].text("Thumbnails");
60
        _display_menu[4].text("Sort by name");
61
        _display_menu[5].text("Sort by type");
62
        _display_menu[6].text("Sort by size");
63
        _display_menu[7].text("Sort by date");
64
        _display_menu[3].separator(1);
65
 
66
        _rename_leafname=new char[256];
67
        _rename_leafname[0]=0;
68
        _rename_menu[0].text(_rename_leafname,256);
69
        _rename_menu[0].writable(1);
70
 
71
        _access_menu[0].text("Protected");
72
        _access_menu[1].text("Unprotected");
73
        _access_menu[2].text("Public");
74
        _access_menu[3].text("Private");
75
        _access_menu[4].text("Access details");
52 gdshaw@RISCPKG.ORG 76
        _access_menu[4].dbox(_access_dbox);
31 gdshaw@RISCPKG.ORG 77
        _access_menu[1].separator(1);
78
        _access_menu[3].separator(1);
79
 
80
        _find_pattern=new char[256];
81
        _find_pattern[0]=0;
82
        _find_menu[0].text(_find_pattern,256);
83
        _find_menu[0].writable(1);
84
 
85
        _filetype_field=new char[16];
86
        _filetype_field[0]=0;
87
        _filetype_menu[0].text(_filetype_field,16);
88
        _filetype_menu[0].writable(1);
89
 
90
        _selection_menu[0].text("Copy");
91
        _selection_menu[1].text("Rename");
92
        _selection_menu[2].text("Delete");
93
        _selection_menu[3].text("Access");
94
        _selection_menu[4].text("Count");
95
        _selection_menu[5].text("Help");
96
        _selection_menu[6].text("Info");
97
        _selection_menu[7].text("Find");
98
        _selection_menu[8].text("Set type");
99
        _selection_menu[9].text("Stamp");
100
        _selection_menu[10].text("Share");
49 gdshaw@RISCPKG.ORG 101
        _selection_menu[0].dbox(_copy_dbox);
31 gdshaw@RISCPKG.ORG 102
        _selection_menu[1].submenu(_rename_menu);
103
        _selection_menu[3].submenu(_access_menu);
41 gdshaw@RISCPKG.ORG 104
        _selection_menu[6].dbox(_info_dbox);
31 gdshaw@RISCPKG.ORG 105
        _selection_menu[7].submenu(_find_menu);
106
        _selection_menu[8].submenu(_filetype_menu);
107
 
108
        _options_menu[0].text("Confirm all");
109
        _options_menu[1].text("Confirm deletes");
110
        _options_menu[2].text("Verbose");
111
        _options_menu[3].text("Force");
112
        _options_menu[4].text("Newer");
113
        _options_menu[5].text("Faster");
114
        _options_menu[1].separator(1);
115
 
116
        *_selection_text=0;
117
        (*this)[0].text("Display");
118
        (*this)[1].text(_selection_text);
119
        (*this)[2].text("Select all");
120
        (*this)[3].text("Clear selection");
121
        (*this)[4].text("Options");
122
        (*this)[5].text("New directory");
123
        (*this)[6].text("Set work directory");
124
        (*this)[7].text("Open parent");
125
        (*this)[8].text("Refresh");
126
        (*this)[0].submenu(_display_menu);
127
        (*this)[1].submenu(_selection_menu);
128
        (*this)[4].submenu(_options_menu);
38 gdshaw@RISCPKG.ORG 129
        (*this)[5].dbox(_new_dir_dbox);
31 gdshaw@RISCPKG.ORG 130
}
131
 
132
filer_menu::~filer_menu()
133
{
134
        delete[] _rename_leafname;
135
        delete[] _find_pattern;
136
        delete[] _filetype_field;
137
        delete[] _selection_text;
138
        delete[] _layout_methods;
139
        delete[] _sort_methods;
140
}
141
 
142
void filer_menu::handle_menu_selection(wimp_selection& block)
143
{
144
        // Ensure that handler can never be executed when the menu
145
        // has no owner.
146
        if (!_owner) return;
147
 
148
        // Get mouse button state for later use.
149
        // (Do this at an early stage in case it changes.)
150
        wimp_pointer info;
151
        xwimp_get_pointer_info(&info);
152
 
36 gdshaw@RISCPKG.ORG 153
        // Get copy of filer options.
154
        // (This needs to be a copy so that it can be modified,
155
        // and so that the filer window can see what has changed.)
156
        filer_options options(_owner->options());
157
 
32 gdshaw@RISCPKG.ORG 158
        switch (block.items[0])
159
        {
36 gdshaw@RISCPKG.ORG 160
        case 0: /* display */
161
                switch (block.items[1])
162
                {
163
                case 0:
164
                case 1:
165
                case 2:
166
                        options.layout(*_layout_methods[block.items[1]]);
167
                        _owner->options(options);
168
                        update_layout();
169
                        break;
170
                case 4:
171
                case 5:
172
                case 6:
173
                case 7:
174
                        options.sort(*_sort_methods[block.items[1]-4]);
175
                        _owner->options(options);
176
                        update_sort();
177
                        break;
178
                }
179
                break;
41 gdshaw@RISCPKG.ORG 180
        case 1: /* object */
181
                switch (block.items[1])
182
                {
48 gdshaw@RISCPKG.ORG 183
                case 1: /* rename */
184
                        switch (block.items[2])
185
                        {
186
                        case 0:
187
                                if (_object&&strlen(_rename_leafname))
188
                                {
189
                                        const size_t max_pathname=255;
190
                                        size_t max_leafname=
191
                                                max_pathname-strlen(_owner->pathname())-1;
192
                                        if (max_leafname>max_pathname) max_leafname=0;
193
                                        if ((strlen(_object->name)<=max_leafname)&&
194
                                                (strlen(_rename_leafname)<=max_leafname))
195
                                        {
196
                                                static char src_pathname[max_pathname+1];
197
                                                static char dst_pathname[max_pathname+1];
198
                                                sprintf(src_pathname,"%s.%s",_owner->pathname(),
199
                                                        _object->name);
200
                                                sprintf(dst_pathname,"%s.%s",_owner->pathname(),
201
                                                        _rename_leafname);
202
                                                osfscontrol_rename(src_pathname,dst_pathname);
203
                                        }
204
                                }
205
                                break;
206
                        }
207
                        break;
46 gdshaw@RISCPKG.ORG 208
                case 2: /* delete */
209
                        {
210
                                wimp_t handle=_owner->begin_filer_action();
211
                                xfileractionsendstartoperation_delete(handle,
212
                                        _owner->options().flags()|fileraction_RECURSE);
213
                        }
214
                        break;
52 gdshaw@RISCPKG.ORG 215
                case 3: /* access */
216
                        switch (block.items[2])
217
                        {
218
                        case 0: /* protected */
219
                        case 1: /* unprotected */
220
                        case 2: /* public */
221
                        case 3: /* private */
222
                                {
223
                                        int attr_bits=0;
224
                                        int attr_mask=0xffff;
225
                                        switch (block.items[2])
226
                                        {
227
                                        case 0: /* protected */
228
                                                attr_bits|=
229
                                                        fileswitch_ATTR_OWNER_LOCKED;
230
                                                attr_mask&=~(
231
                                                        fileswitch_ATTR_OWNER_WRITE|
232
                                                        fileswitch_ATTR_OWNER_LOCKED|
233
                                                        fileswitch_ATTR_WORLD_WRITE);
234
                                                break;
235
                                        case 1: /* unprotected */
236
                                                attr_bits|=
237
                                                        fileswitch_ATTR_OWNER_WRITE;
238
                                                attr_mask&=~(
239
                                                        fileswitch_ATTR_OWNER_WRITE|
240
                                                        fileswitch_ATTR_OWNER_LOCKED);
241
                                                break;
242
                                        case 2: /* public */
243
                                                attr_bits|=
244
                                                        fileswitch_ATTR_WORLD_READ;
245
                                                attr_mask&=~(
246
                                                        fileswitch_ATTR_WORLD_READ);
247
                                                break;
248
                                        case 3: /* private */
249
                                                attr_mask&=~(
250
                                                        fileswitch_ATTR_WORLD_READ|
251
                                                        fileswitch_ATTR_WORLD_WRITE);
252
                                                break;
253
                                        }
254
                                        int attr=attr_bits|(attr_mask<<16);
255
                                        wimp_t handle=_owner->begin_filer_action();
256
                                        xfileractionsendstartoperation_access(handle,
257
                                                _owner->options().flags()|fileraction_RECURSE,&attr);
258
                                }
259
                        }
260
                        break;
45 gdshaw@RISCPKG.ORG 261
                case 4: /* count */
262
                        {
263
                                wimp_t handle=_owner->begin_filer_action();
264
                                xfileractionsendstartoperation_count(handle,
265
                                        _owner->options().flags()|fileraction_RECURSE);
266
                        }
267
                        break;
41 gdshaw@RISCPKG.ORG 268
                case 6: /* info */
269
                        break;
50 gdshaw@RISCPKG.ORG 270
                case 7: /* find */
271
                        {
272
                                wimp_t handle=_owner->begin_filer_action();
273
                                xfileractionsendstartoperation_find(handle,
274
                                        _owner->options().flags()|fileraction_RECURSE,
275
                                        _find_pattern,strlen(_find_pattern));
276
                        }
277
                        break;
51 gdshaw@RISCPKG.ORG 278
                case 8: /* set type */
279
                        {
280
                                bits filetype=
281
                                        osfscontrol_file_type_from_string(_filetype_field);
282
                                wimp_t handle=_owner->begin_filer_action();
283
                                xfileractionsendstartoperation_set_type(handle,
284
                                        _owner->options().flags()|fileraction_RECURSE,&filetype);
285
                        }
286
                        break;
47 gdshaw@RISCPKG.ORG 287
                case 9: /* stamp */
288
                        {
289
                                wimp_t handle=_owner->begin_filer_action();
290
                                xfileractionsendstartoperation_stamp(handle,
291
                                        _owner->options().flags()|fileraction_RECURSE);
292
                        }
293
                        break;
48 gdshaw@RISCPKG.ORG 294
                }
45 gdshaw@RISCPKG.ORG 295
                break;
33 gdshaw@RISCPKG.ORG 296
        case 2: /* select all */
32 gdshaw@RISCPKG.ORG 297
                _owner->select_all(1);
298
                break;
33 gdshaw@RISCPKG.ORG 299
        case 3: /* clear selection */
32 gdshaw@RISCPKG.ORG 300
                _owner->select_all(0);
301
                break;
43 gdshaw@RISCPKG.ORG 302
        case 4: /* options */
303
                {
304
                        filer_options options(_owner->options());
305
                        switch (block.items[1])
306
                        {
307
                        case 0:
308
                                options.flags(fileraction_CONFIRM,
309
                                        fileraction_CONFIRM_DELETES_ONLY);
310
                                break;
311
                        case 1:
312
                                options.flags(fileraction_CONFIRM_DELETES_ONLY,
313
                                        fileraction_CONFIRM);
314
                                break;
315
                        case 2:
316
                                options.flags(fileraction_VERBOSE,0);
317
                                break;
318
                        case 3:
319
                                options.flags(fileraction_FORCE,0);
320
                                break;
321
                        case 4:
322
                                options.flags(fileraction_NEWER,0);
323
                                break;
324
                        case 5:
325
                                options.flags(fileraction_FASTER,0);
326
                                break;
327
                        }
328
                        _owner->options(options);
329
                        update_options();
330
                }
331
                break;
42 gdshaw@RISCPKG.ORG 332
        case 6: /* set work directory */
333
                _owner->set_work_directory();
334
                break;
33 gdshaw@RISCPKG.ORG 335
        case 7: /* open parent */
336
                {
337
                        os_coord offset;
338
                        offset.x=-20;
339
                        offset.y=32;
340
                        _owner->open_parent(offset);
341
                }
342
                break;
34 gdshaw@RISCPKG.ORG 343
        case 8: /* refresh */
344
                _owner->refresh();
345
                break;
32 gdshaw@RISCPKG.ORG 346
        }
347
 
31 gdshaw@RISCPKG.ORG 348
        if (info.buttons&1)
349
        {
350
                // If selection made with adjust click then re-open menu.
351
                menu::show(info);
352
        }
353
        else
354
        {
355
                // Otherwise, allow menu to close but inform filer window.
356
                if (_owner) _owner->handle_menus_deleted();
357
        }
358
}
359
 
360
void filer_menu::handle_menus_deleted()
361
{
362
        // If menu is closed, other than because of a selection, then
363
        // inform filer window.
364
        if (_owner) _owner->handle_menus_deleted();
365
}
366
 
367
void filer_menu::show(wimp_pointer& block)
368
{
369
        menu::show(block);
370
}
371
 
372
void filer_menu::update_layout()
373
{
374
        // Iterate over the list of layout methods.
375
        // Tick the one which matches the window, untick the others.
376
        layout_method& method=_owner->options().layout();
377
        for (unsigned int i=0;i!=_layout_count;++i)
378
                _display_menu[i].tick(_layout_methods[i]==&method);
379
}
380
 
381
void filer_menu::update_sort()
382
{
383
        sort_method& method=_owner->options().sort();
384
        for (unsigned int i=0;i!=_sort_count;++i)
385
                _display_menu[4+i].tick(_sort_methods[i]==&method);
386
}
387
 
388
void filer_menu::update_selection()
389
{
390
        // Fetch selection and temporary selection.
391
        directory& objects=_owner->selection();
392
        unsigned int temp_selection=_owner->temp_selection();
393
 
394
        // Iterate over selection, counting objects and distinct filetypes
395
        // (in the latter case, distinguishing only between 0, 1 and some).
396
        // If there is only one selected object then record its identity.
397
        // Similarly if there is only one distinct filetype.
398
        osgbpb_info* object=0;
399
        unsigned int count=0;
400
        bits filetype_last=(bits)-1;
401
        int filetype_count=0;
402
        for (int i=0,i_end=objects.size();i!=i_end;++i)
403
        {
404
                if (objects[i].selected())
405
                {
406
                        // Count selected objects.
407
                        osgbpb_info& info=*objects[i];
408
                        switch (info.obj_type)
409
                        {
410
                        case fileswitch_IS_FILE:
411
                        case fileswitch_IS_DIR:
412
                        case fileswitch_IS_IMAGE:
413
                                object=&info;
414
                                ++count;
415
                                break;
416
                        }
417
 
418
                        // Count distinct filetypes.
419
                        // In fact what is recorded is the number of times the filetype
420
                        // changes whilst iterating through the selection, which is
421
                        // useful enough but easier to compute.
422
                        bits filetype=(bits)-1;
423
                        xosfscontrol_info_to_file_type(info.name,info.load_addr,
424
                                info.exec_addr,info.size,info.attr,info.obj_type,&filetype);
425
                        if (filetype!=filetype_last)
426
                        {
427
                                filetype_last=filetype;
428
                                ++filetype_count;
429
                        }
430
                }
431
        }
432
 
433
        // Determine how many objects were counted because they are part of
434
        // a temporary selection.  (This will be either 0 or 1.)
435
        unsigned int temp_count=0;
436
        if ((temp_selection!=directory::npos)&&
437
                (objects[temp_selection].selected())) ++temp_count;
438
 
439
        // Update text of menu entry leading to selection submenu.
440
        if (count==0)
441
        {
442
                // No objects selected.
443
                _object=0;
444
                std::sprintf(_selection_text,"File ''");
52 gdshaw@RISCPKG.ORG 445
                _access_dbox.update(0);
31 gdshaw@RISCPKG.ORG 446
        }
447
        else if (count==1)
448
        {
449
                // One object selected.
450
                // Text depends on whether it is a file, directory, image file,
451
                // or application directory.
452
                _object=object;
453
                const char* fmt="";
454
                switch (object->obj_type)
455
                {
456
                case fileswitch_IS_FILE:
457
                        fmt="File '%.*s'";
458
                        break;
459
                case fileswitch_IS_IMAGE:
460
                        if (object->name[0]=='!') fmt="App. '%.*s'";
461
                        else fmt="Image '%.*s'";
462
                        break;
463
                case fileswitch_IS_DIR:
464
                        if (object->name[0]=='!') fmt="App. '%.*s'";
465
                        else fmt="Dir. '%.*s'";
466
                        break;
467
                }
468
                std::sprintf(_selection_text,fmt,max_name_length,object->name);
41 gdshaw@RISCPKG.ORG 469
 
470
                // Update dialogue boxes.
49 gdshaw@RISCPKG.ORG 471
                _copy_dbox.update(*object);
52 gdshaw@RISCPKG.ORG 472
                _access_dbox.update(object);
41 gdshaw@RISCPKG.ORG 473
                _info_dbox.update(*object);
31 gdshaw@RISCPKG.ORG 474
        }
475
        else
476
        {
477
                // More than one object selected.
478
                _object=0;
479
                std::sprintf(_selection_text,"Selection");
52 gdshaw@RISCPKG.ORG 480
                _access_dbox.update(0);
31 gdshaw@RISCPKG.ORG 481
        }
482
 
483
        // Disable selection submenu if nothing selected.
484
        // Disable select all if everything already selected.
485
        // Disable clear selection if nothing selected.
486
        // (Disregard any temporary selection.)
487
        (*this)[1].disabled(count==0);
488
        (*this)[2].disabled(count-temp_count==objects.size());
489
        (*this)[3].disabled(count-temp_count==0);
490
 
491
        // Disable copy and rename options unless exactly one object selected.
492
        _selection_menu[0].disabled(count!=1);
493
        _selection_menu[1].disabled(count!=1);
494
 
495
        // Disable help option unless exactly one application selected.
496
        _selection_menu[5].disabled(!(count==1&&
497
                (object->obj_type&fileswitch_IS_DIR)&&object->name[0]=='!'));
498
 
499
        // Disable info option unless exactly one object selected.
500
        _selection_menu[6].disabled(count!=1);
501
 
502
        // Populate filetype field.
503
        if ((filetype_count==1)&&(filetype_last<0x1000))
504
        {
505
                union
506
                {
507
                        bits words[2];
508
                        char chars[9];
509
                };
510
                xosfscontrol_read_file_type(filetype_last,&words[0],&words[1]);
511
                int i=8;
512
                while (i&&chars[i-1]==' ') --i;
513
                chars[i]=0;
514
                std::strcpy(_filetype_field,chars);
515
        }
516
        else _filetype_field[0]=0;
517
}
518
 
519
void filer_menu::update_pathname()
520
{
521
        // Disable open parent option if already at root.
522
        char* pathname=(char*)_owner->pathname();
523
        char* lastdot=strrchr(pathname,'.');
524
        int isroot=!(lastdot&&strcmp(lastdot+1,"$"));
525
        (*this)[7].disabled(isroot);
526
}
527
 
43 gdshaw@RISCPKG.ORG 528
void filer_menu::update_options()
529
{
530
        fileraction_flags flags=_owner->options().flags();
531
        _options_menu[0].tick(flags&fileraction_CONFIRM);
532
        _options_menu[1].tick(flags&fileraction_CONFIRM_DELETES_ONLY);
533
        _options_menu[2].tick(flags&fileraction_VERBOSE);
534
        _options_menu[3].tick(flags&fileraction_FORCE);
535
        _options_menu[4].tick(flags&fileraction_NEWER);
536
        _options_menu[5].tick(flags&fileraction_FASTER);
537
}
538
 
31 gdshaw@RISCPKG.ORG 539
void filer_menu::update(filer_window* owner)
540
{
541
        _owner=owner;
542
        if (_owner)
543
        {
49 gdshaw@RISCPKG.ORG 544
                _new_dir_dbox.owner(_owner);
545
                _copy_dbox.owner(_owner);
52 gdshaw@RISCPKG.ORG 546
                _access_dbox.owner(_owner);
31 gdshaw@RISCPKG.ORG 547
                update_layout();
548
                update_sort();
549
                update_selection();
550
                update_pathname();
43 gdshaw@RISCPKG.ORG 551
                update_options();
31 gdshaw@RISCPKG.ORG 552
        }
553
}