Subversion Repositories Filer-Free

Rev

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