Subversion Repositories Filer-Free

Rev

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