Subversion Repositories Filer-Free

Rev

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

Rev Author Line No. Line
21 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>
35 gdshaw@RISCPKG.ORG 7
#include <cstdio>
21 gdshaw@RISCPKG.ORG 8
 
35 gdshaw@RISCPKG.ORG 9
#include "oslib/osbyte.h"
10
#include "oslib/wimp.h"
11
 
21 gdshaw@RISCPKG.ORG 12
#include "template_loader.h"
25 gdshaw@RISCPKG.ORG 13
#include "layout_method.h"
31 gdshaw@RISCPKG.ORG 14
#include "filer_menu.h"
21 gdshaw@RISCPKG.ORG 15
#include "filer_window.h"
16
#include "filer_application.h"
17
 
35 gdshaw@RISCPKG.ORG 18
namespace {
19
 
20
/** The maximum length of a leafname. */
21
const size_t max_name_length=256;
22
 
23
/** The initial horizontal offset for opening a subdirectory. */
24
const int open_xstart=20;
25
 
26
/** The initial vertical offset for opening a subdirectory. */
27
const int open_ystart=-32;
28
 
29
/** The horizontal offset between subdirectories. */
30
const int open_xstep=20;
31
 
32
/** The vertical offset between subdirectories. */
33
const int open_ystep=0;
34
 
35
/** The number of distinct subdirectory offsets. */
36
const unsigned int open_count=8;
37
 
38
}; /* anonymous namespace */
39
 
21 gdshaw@RISCPKG.ORG 40
filer_window::filer_window(filer_application* app,const char* pathname,
25 gdshaw@RISCPKG.ORG 41
        const os_box& box,const filer_options& options):
21 gdshaw@RISCPKG.ORG 42
        window(app),
43
        _pathname(new char[std::strlen(pathname)+1]),
25 gdshaw@RISCPKG.ORG 44
        _options(options),
45
        _directory(options.sort()),
46
        _xcsize(68),
47
        _ycsize(68),
48
        _xccount(1),
31 gdshaw@RISCPKG.ORG 49
        _yccount(1),
35 gdshaw@RISCPKG.ORG 50
        _temp_selection(directory::npos),
51
        _auto_pos(open_xstart,open_ystart,open_xstep,open_ystep,open_count)
21 gdshaw@RISCPKG.ORG 52
{
53
        // Copy pathname into buffer owned by this object.
54
        std::strcpy(_pathname,pathname);
55
 
56
        // Load directory listing.
57
        reload();
58
 
59
        // Create window from template.
60
        template_loader loader("directory","Resources:$.Resources.Filer.Templates");
61
        wimp_window& wblock=loader;
62
        wblock.title_flags|=wimp_ICON_INDIRECTED;
63
        wblock.title_data.indirected_text.text=_pathname;
64
        wblock.title_data.indirected_text.validation="";
65
        wblock.title_data.indirected_text.size=strlen(_pathname);
66
        wblock.visible=box;
67
        wblock.extent.x0=0;
68
        wblock.extent.y0=-(box.y1-box.y0);
69
        wblock.extent.x1=box.x1-box.x0;
70
        wblock.extent.y1=0;
28 gdshaw@RISCPKG.ORG 71
        wblock.flags|=wimp_WINDOW_IGNORE_XEXTENT;
21 gdshaw@RISCPKG.ORG 72
        wblock.icon_count=0;
73
        create_window(wblock);
74
 
75
        // Resize extent to match actual visible area.
76
        wimp_window_state wstate;
77
        get_window_state(wstate);
78
        os_box extent;
79
        extent.x0=0;
80
        extent.y0=-(wstate.visible.y1-wstate.visible.y0);
81
        extent.x1=wstate.visible.x1-wstate.visible.x0;
82
        extent.y1=0;
83
        window::set_extent(extent);
84
 
25 gdshaw@RISCPKG.ORG 85
        // Open window, but keep it hidden from view.
21 gdshaw@RISCPKG.ORG 86
        wimp_window_info winfo;
87
        get_window_info_header(winfo);
88
        wimp_open open;
89
        open.visible.x0=winfo.visible.x0;
90
        open.visible.y0=winfo.visible.y1-(winfo.extent.y1-winfo.extent.y0);
91
        open.visible.x1=winfo.visible.x0+(winfo.extent.x1-winfo.extent.x0);
92
        open.visible.y1=winfo.visible.y1;
93
        open.visible=winfo.visible;
94
        open.xscroll=0;
95
        open.yscroll=0;
25 gdshaw@RISCPKG.ORG 96
        open.next=wimp_HIDDEN;
97
        open_window(open);
98
 
99
        // Reformat content of window.
100
        reformat();
101
 
102
        // Reopen window, now making it visible.
103
        get_window_state(wstate);
104
        open.visible=wstate.visible;
105
        open.xscroll=wstate.xscroll;
106
        open.yscroll=wstate.yscroll;
21 gdshaw@RISCPKG.ORG 107
        open.next=wimp_TOP;
108
        open_window(open);
109
}
110
 
111
filer_window::~filer_window()
112
{
113
        // Delete pathname buffer.
114
        delete[] _pathname;
115
}
116
 
117
void filer_window::handle_redraw_request(wimp_draw& block)
118
{
25 gdshaw@RISCPKG.ORG 119
        // Fetch layout.
120
        layout_method& layout=_options.layout();
121
        int xgap=layout.xgap();
122
        int ygap=layout.ygap();
123
 
21 gdshaw@RISCPKG.ORG 124
        int more=0;
125
        xwimp_redraw_window(&block,&more);
126
        while (more)
127
        {
25 gdshaw@RISCPKG.ORG 128
                // Get clipping coordinates with respect to this window.
129
                int xmin=block.clip.x0-block.box.x0+block.xscroll-xgap;
130
                int ymin=block.clip.y0-block.box.y1+block.yscroll+ygap;
131
                int xmax=block.clip.x1-block.box.x0+block.xscroll-xgap;
132
                int ymax=block.clip.y1-block.box.y1+block.yscroll+ygap;
133
 
134
                // Determine which cells are within clipping box.
135
                int xcmin=(xmin)/(_xcsize+xgap);
136
                int ycmin=(-ymax)/(_ycsize+ygap);
137
                int xcmax=(xmax-1)/(_xcsize+xgap)+1;
138
                int ycmax=(-ymin-1)/(_ycsize+ygap)+1;
139
                if (xcmin<0) xcmin=0;
140
                if (ycmin<0) ycmin=0;
141
                if (xcmax>_xccount) xcmax=_xccount;
142
                if (ycmax>_yccount) ycmax=_yccount;
143
 
144
                // Iterate over cells to be redrawn.
145
                for (int yc=ycmin;yc<ycmax;++yc)
146
                {
147
                        for (int xc=xcmin;xc<xcmax;++xc)
148
                        {
149
                                // Determine whether cell is occupied.
30 gdshaw@RISCPKG.ORG 150
                                unsigned int i=xc+yc*_xccount;
151
                                if (i<_directory.size())
25 gdshaw@RISCPKG.ORG 152
                                {
153
                                        // Redraw cell.
154
                                        os_box box;
155
                                        box.x0=xc*(_xcsize+xgap)+xgap;
156
                                        box.y0=-yc*(_ycsize+ygap)-_ycsize-ygap;
157
                                        box.x1=box.x0+_xcsize;
158
                                        box.y1=box.y0+_ycsize;
159
                                        layout.plot(box,*_directory[i],
160
                                                _directory[i].selected(),_options);
161
                                }
162
                        }
163
                }
164
 
21 gdshaw@RISCPKG.ORG 165
                xwimp_get_rectangle(&block,&more);
166
        }
167
}
168
 
28 gdshaw@RISCPKG.ORG 169
void filer_window::handle_open_request(wimp_open& block)
170
{
171
        int xccount=_xccount;
172
        int yccount=_yccount;
173
        reformat(block.visible.x1-block.visible.x0);
174
        xwimp_open_window(&block);
175
        if (_xccount!=xccount||_yccount!=yccount) force_redraw();
176
}
177
 
29 gdshaw@RISCPKG.ORG 178
void filer_window::handle_close_request(wimp_close& block)
179
{
180
        wimp_pointer pointer;
181
        xwimp_get_pointer_info(&pointer);
182
 
183
        if (pointer.buttons&wimp_CLICK_ADJUST)
184
        {
185
                os_coord offset;
186
                offset.x=0;
187
                offset.y=0;
188
                open_parent(offset);
189
        }
190
 
191
        delete this;
192
}
193
 
30 gdshaw@RISCPKG.ORG 194
void filer_window::handle_mouse_click(wimp_pointer& block)
195
{
196
        // Fetch layout.
197
        layout_method& layout=options().layout();
198
 
35 gdshaw@RISCPKG.ORG 199
        // Read shift state.
200
        int shift_state=0;
201
        xosbyte1(osbyte_VAR_KEYBOARD_STATE,0,0xff,&shift_state);
202
        int shift=(shift_state&0x08)!=0;
203
 
30 gdshaw@RISCPKG.ORG 204
        // Find cell under pointer.
205
        unsigned int index=find_cell(block.pos);
206
 
35 gdshaw@RISCPKG.ORG 207
        bool close=false;
31 gdshaw@RISCPKG.ORG 208
        if (block.buttons==wimp_CLICK_MENU)
30 gdshaw@RISCPKG.ORG 209
        {
31 gdshaw@RISCPKG.ORG 210
                // Any existing menu will be deleted by this action.
211
                handle_menus_deleted();
212
 
213
                // Count number of cells selected.
214
                int count=0;
215
                for (unsigned int i=0,i_end=_directory.size();i!=i_end;++i)
216
                {
217
                        if (_directory[i].selected()) ++count;
218
                }
219
 
220
                // If pointer is over a cell and nothing has yet been selected
221
                // then temporarily select the cell under the pointer.
222
                if (index!=directory::npos&&!_directory[index].selected()&&!count)
223
                {
224
                        _directory[index].selected(true);
225
                        _temp_selection=index;
226
                        force_redraw_cell(index);
227
                }
228
 
229
                // Update then open the menu.
230
                shared_menu().update(this);
231
                shared_menu().show(block);
232
        }
35 gdshaw@RISCPKG.ORG 233
        else if (block.buttons&(wimp_DOUBLE_ADJUST|wimp_DOUBLE_SELECT))
234
        {
235
                // If double click within a cell then run that object.
236
                if (index!=directory::npos)
237
                {
238
                        select_all(0);
239
                        close=(block.buttons&wimp_DOUBLE_ADJUST)!=0;
240
                        if (close) close_window();
241
                        run(*_directory[index],close,shift);
242
                }
243
        }
31 gdshaw@RISCPKG.ORG 244
        else if (block.buttons&(wimp_SINGLE_SELECT|wimp_SINGLE_ADJUST))
245
        {
30 gdshaw@RISCPKG.ORG 246
                // If single select click then first de-select everything.
247
                if (block.buttons&wimp_SINGLE_SELECT) select_all(0);
248
 
249
                // If click within a cell then select that cell.
250
                if (index!=directory::npos)
251
                {
252
                        _directory[index].selected(!_directory[index].selected());
253
                        force_redraw_cell(index);
254
                }
255
        }
35 gdshaw@RISCPKG.ORG 256
 
257
        // If this window has closed as a result of the mouse event
258
        // then delete it.
259
        if (close) delete this;
30 gdshaw@RISCPKG.ORG 260
}
261
 
31 gdshaw@RISCPKG.ORG 262
void filer_window::handle_menus_deleted()
263
{
264
        if (_temp_selection!=directory::npos)
265
        {
266
                _directory[_temp_selection].selected(0);
267
                force_redraw_cell(_temp_selection);
268
                _temp_selection=directory::npos;
269
        }
270
}
271
 
36 gdshaw@RISCPKG.ORG 272
void filer_window::options(const filer_options& options)
273
{
274
        // Keep track of whether window needs to be reformatted, restored
275
        // and redrawn.
276
        int need_reformat=0;
277
        int need_resort=0;
278
        int need_redraw=0;
279
 
280
        // Check whether layout method has changed.
281
        if (&options.layout()!=&_options.layout())
282
        {
283
                need_reformat=1;
284
                need_redraw=1;
285
        }
286
 
287
        // Check whether sorting method has changed.
288
        if (&options.sort()!=&_options.sort())
289
        {
290
                need_resort=1;
291
                need_redraw=1;
292
        }
293
 
294
        // Update options.
295
        _options=options;
296
 
297
        // Reformat, resort and redraw window as required.
298
        if (need_resort) _directory.sort(_options.sort());
299
        if (need_redraw) force_redraw();
300
        if (need_reformat)
301
        {
302
                reformat();
303
                if (need_redraw) force_redraw();
304
        }
305
}
306
 
31 gdshaw@RISCPKG.ORG 307
filer_menu& filer_window::shared_menu() const
308
{
309
        static filer_menu* _shared_menu=0;
310
        if (!_shared_menu)
311
        {
312
                _shared_menu=new filer_menu(parent_application());
313
        }
314
        return *_shared_menu;
315
}
316
 
21 gdshaw@RISCPKG.ORG 317
void filer_window::reload()
318
{
319
        static int buffer[256];
320
        _directory.load(_pathname,buffer,sizeof(buffer));
321
}
25 gdshaw@RISCPKG.ORG 322
 
323
void filer_window::reformat()
324
{
325
        wimp_window_state wstate;
326
        get_window_state(wstate);
327
        reformat(wstate.visible.x1-wstate.visible.x0);
328
}
329
 
330
void filer_window::reformat(int xsize)
331
{
332
        // Fetch layout.
333
        layout_method& layout=options().layout();
334
        int xgap=layout.xgap();
335
        int ygap=layout.ygap();
336
 
337
        // Calculate cell size.  This must be able to accommodate
338
        // - the minimum cell size for the chosen layout, and
339
        // - the actual size occupied by each file to be listed.
340
        os_coord csize=layout.min_size(_options);
341
        for (unsigned int i=0;i!=_directory.size();++i)
342
        {
343
                os_coord size=layout.size(*_directory[i],_options);
344
                if (size.x>csize.x) csize.x=size.x;
345
                if (size.y>csize.y) csize.y=size.y;
346
        }
347
 
348
        // Apply the calculated cell size.
349
        _xcsize=csize.x;
350
        _ycsize=csize.y;
351
 
352
        // Determine number of rows and columns.
353
        _xccount=xsize/(_xcsize+xgap);
354
        if (_xccount<1) _xccount=1;
355
        _yccount=(_directory.size()+_xccount-1)/_xccount;
356
        if (_yccount<1) _yccount=1;
357
 
358
        // Calculate new extent.
359
        int extent_xsize=_xccount*(_xcsize+xgap)+xgap;
360
        int extent_ysize=_yccount*(_ycsize+ygap)+ygap;
361
 
28 gdshaw@RISCPKG.ORG 362
        // Do not force the window to become narrower than the width requested,
25 gdshaw@RISCPKG.ORG 363
        // unless there are too few directory entries to justify that width.
364
        if (extent_xsize<xsize)
365
        {
366
                extent_xsize=xsize;
367
                if (((extent_xsize+_xcsize-1)/(_xcsize+xgap))>
368
                        static_cast<int>(_directory.size()))
369
                {
370
                        extent_xsize=_directory.size()*(_xcsize+xgap)+xgap;
371
                }
372
        }
373
 
374
        // Apply the calculated extent.
375
        os_box extent;
376
        extent.x0=0;
377
        extent.y0=-extent_ysize;
378
        extent.x1=extent_xsize;
379
        extent.y1=0;
380
        set_extent(extent);
381
}
29 gdshaw@RISCPKG.ORG 382
 
30 gdshaw@RISCPKG.ORG 383
unsigned int filer_window::find_cell(const os_coord& p)
384
{
385
        layout_method& layout=options().layout();
386
        int xgap=layout.xgap();
387
        int ygap=layout.ygap();
388
 
389
        wimp_window_state state;
390
        get_window_state(state);
391
        int x=p.x-state.visible.x0+state.xscroll-xgap;
392
        int y=p.y-state.visible.y1+state.yscroll+ygap;
393
 
394
        int xc=x/(_xcsize+xgap);
395
        int yc=(-y)/(_ycsize+ygap);
396
        unsigned int index=xc+yc*_xccount;
397
 
398
        x-=xc*(_xcsize+xgap);
399
        y+=yc*(_ycsize+ygap);
400
 
401
        int found=(x>=0)&&(y>=-_ycsize)&&(x<_xcsize)&&(y<0)&&
402
                (xc>=0)&&(yc>=0)&&(xc<_xccount)&&(yc<_yccount)&&
403
                (index<_directory.size());
404
 
405
        return (found)?index:directory::npos;
406
}
407
 
408
void filer_window::force_redraw_cell(unsigned int index)
409
{
410
        layout_method& layout=options().layout();
411
        int xgap=layout.xgap();
412
        int ygap=layout.ygap();
413
 
414
        int xc=index%_xccount;
415
        int yc=index/_xccount;
416
 
417
        os_box box;
418
        box.x0=xgap+xc*(_xcsize+xgap);
419
        box.y0=-(yc+1)*(_ycsize+ygap);
420
        box.x1=box.x0+_xcsize;
421
        box.y1=box.y0+_ycsize;
422
        force_redraw(box);
423
}
424
 
34 gdshaw@RISCPKG.ORG 425
void filer_window::refresh()
426
{
427
        reload();
428
        reformat();
429
        force_redraw();
430
}
431
 
30 gdshaw@RISCPKG.ORG 432
void filer_window::select_all(int selected)
433
{
434
        layout_method& layout=options().layout();
435
        int xgap=layout.xgap();
436
        int ygap=layout.ygap();
437
 
438
        int xc_min=_xccount;
439
        int yc_min=_yccount;
440
        int xc_max=0;
441
        int yc_max=0;
442
 
443
        for (unsigned int i=0;i!=_directory.size();++i)
444
        {
445
                if (_directory[i].selected()!=selected)
446
                {
447
                        _directory[i].selected(selected);
448
 
449
                        int xc=i%_xccount;
450
                        int yc=i/_xccount;
451
                        if (xc<xc_min) xc_min=xc;
452
                        if (yc<yc_min) yc_min=yc;
453
                        if (xc+1>xc_max) xc_max=xc+1;
454
                        if (yc+1>yc_max) yc_max=yc+1;
455
                }
456
        }
457
 
458
        if ((xc_max>xc_min)&&(yc_max>yc_min))
459
        {
460
                os_box box;
461
                box.x0=xgap+xc_min*(_xcsize+xgap);
462
                box.y0=-yc_max*(_ycsize+ygap);
463
                box.x1=xc_max*(_xcsize+xgap);
464
                box.y1=-ygap-yc_min*(_ycsize+ygap);
465
                force_redraw(box);
466
        }
31 gdshaw@RISCPKG.ORG 467
 
468
        _temp_selection=directory::npos;
30 gdshaw@RISCPKG.ORG 469
}
470
 
29 gdshaw@RISCPKG.ORG 471
void filer_window::open_parent(const os_coord& offset) const
472
{
473
        // Determine whether there is a parent directory.
474
        char* lastdot=strrchr(_pathname,'.');
475
        if (lastdot&&strcmp(lastdot+1,"$"))
476
        {
477
                // Find top-left corner of this window.
478
                wimp_window_state state;
479
                get_window_state(state);
480
 
481
                // Add offset to give top-left corner of new window.
482
                os_box box;
483
                box.x0=state.visible.x0+offset.x;
484
                box.y1=state.visible.y1+offset.y;
485
                box.x1=box.x0;
486
                box.y0=box.y1;
487
 
488
                // Open new window.
489
                *lastdot=0;
490
                new filer_window((filer_application*)parent_application(),
491
                        _pathname,box,_options);
492
                *lastdot='.';
493
        }
494
}
35 gdshaw@RISCPKG.ORG 495
 
496
void filer_window::run(osgbpb_info& info,int close,int shift)
497
{
498
        // Need to at least partially handle shift key here, because
499
        // Filer_Run does not allow coordinates to be specified when
500
        // shift-clicking an application directory.
501
        // (... unless there is an alternative way to invoke Filer_Run?)
502
 
503
        static char buffer[12+max_name_length];
504
        if ((info.obj_type&fileswitch_IS_DIR)&&((info.name[0]!='!')||shift))
505
        {
506
                // Get dimensions of this window.
507
                wimp_window_state state;
508
                get_window_state(state);
509
 
510
                // Choose coordinates for new window.
511
                // If closing this window then open in same position,
512
                // otherwise open below and to the right.
513
                os_coord offset=_auto_pos();
514
                os_box box;
515
                box.x0=state.visible.x0+((close)?0:offset.x);
516
                box.y1=state.visible.y1+((close)?0:offset.y);
517
                box.x1=box.x0;
518
                box.y0=box.y1;
519
 
520
                // Open new window.
521
                if (std::strlen(_pathname)+std::strlen(info.name)+1<max_name_length)
522
                {
523
                        std::sprintf(buffer,"%s.%s",_pathname,info.name);
524
                        window* w=new filer_window(
525
                                (filer_application*)parent_application(),
526
                                buffer,box,_options);
527
                }
528
        }
529
        else
530
        {
531
                // Run object.
532
                if (std::strlen(_pathname)+std::strlen(info.name)+1<max_name_length)
533
                {
534
                        std::sprintf(buffer,"Run %s.%s",_pathname,info.name);
535
                        wimp_start_task(buffer);
536
                }
537
        }
538
}