Subversion Repositories Filer-Free

Rev

Rev 30 | Rev 34 | 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>
7
 
8
#include "template_loader.h"
25 gdshaw@RISCPKG.ORG 9
#include "layout_method.h"
31 gdshaw@RISCPKG.ORG 10
#include "filer_menu.h"
21 gdshaw@RISCPKG.ORG 11
#include "filer_window.h"
12
#include "filer_application.h"
13
 
14
filer_window::filer_window(filer_application* app,const char* pathname,
25 gdshaw@RISCPKG.ORG 15
        const os_box& box,const filer_options& options):
21 gdshaw@RISCPKG.ORG 16
        window(app),
17
        _pathname(new char[std::strlen(pathname)+1]),
25 gdshaw@RISCPKG.ORG 18
        _options(options),
19
        _directory(options.sort()),
20
        _xcsize(68),
21
        _ycsize(68),
22
        _xccount(1),
31 gdshaw@RISCPKG.ORG 23
        _yccount(1),
24
        _temp_selection(directory::npos)
21 gdshaw@RISCPKG.ORG 25
{
26
        // Copy pathname into buffer owned by this object.
27
        std::strcpy(_pathname,pathname);
28
 
29
        // Load directory listing.
30
        reload();
31
 
32
        // Create window from template.
33
        template_loader loader("directory","Resources:$.Resources.Filer.Templates");
34
        wimp_window& wblock=loader;
35
        wblock.title_flags|=wimp_ICON_INDIRECTED;
36
        wblock.title_data.indirected_text.text=_pathname;
37
        wblock.title_data.indirected_text.validation="";
38
        wblock.title_data.indirected_text.size=strlen(_pathname);
39
        wblock.visible=box;
40
        wblock.extent.x0=0;
41
        wblock.extent.y0=-(box.y1-box.y0);
42
        wblock.extent.x1=box.x1-box.x0;
43
        wblock.extent.y1=0;
28 gdshaw@RISCPKG.ORG 44
        wblock.flags|=wimp_WINDOW_IGNORE_XEXTENT;
21 gdshaw@RISCPKG.ORG 45
        wblock.icon_count=0;
46
        create_window(wblock);
47
 
48
        // Resize extent to match actual visible area.
49
        wimp_window_state wstate;
50
        get_window_state(wstate);
51
        os_box extent;
52
        extent.x0=0;
53
        extent.y0=-(wstate.visible.y1-wstate.visible.y0);
54
        extent.x1=wstate.visible.x1-wstate.visible.x0;
55
        extent.y1=0;
56
        window::set_extent(extent);
57
 
25 gdshaw@RISCPKG.ORG 58
        // Open window, but keep it hidden from view.
21 gdshaw@RISCPKG.ORG 59
        wimp_window_info winfo;
60
        get_window_info_header(winfo);
61
        wimp_open open;
62
        open.visible.x0=winfo.visible.x0;
63
        open.visible.y0=winfo.visible.y1-(winfo.extent.y1-winfo.extent.y0);
64
        open.visible.x1=winfo.visible.x0+(winfo.extent.x1-winfo.extent.x0);
65
        open.visible.y1=winfo.visible.y1;
66
        open.visible=winfo.visible;
67
        open.xscroll=0;
68
        open.yscroll=0;
25 gdshaw@RISCPKG.ORG 69
        open.next=wimp_HIDDEN;
70
        open_window(open);
71
 
72
        // Reformat content of window.
73
        reformat();
74
 
75
        // Reopen window, now making it visible.
76
        get_window_state(wstate);
77
        open.visible=wstate.visible;
78
        open.xscroll=wstate.xscroll;
79
        open.yscroll=wstate.yscroll;
21 gdshaw@RISCPKG.ORG 80
        open.next=wimp_TOP;
81
        open_window(open);
82
}
83
 
84
filer_window::~filer_window()
85
{
86
        // Delete pathname buffer.
87
        delete[] _pathname;
88
}
89
 
90
void filer_window::handle_redraw_request(wimp_draw& block)
91
{
25 gdshaw@RISCPKG.ORG 92
        // Fetch layout.
93
        layout_method& layout=_options.layout();
94
        int xgap=layout.xgap();
95
        int ygap=layout.ygap();
96
 
21 gdshaw@RISCPKG.ORG 97
        int more=0;
98
        xwimp_redraw_window(&block,&more);
99
        while (more)
100
        {
25 gdshaw@RISCPKG.ORG 101
                // Get clipping coordinates with respect to this window.
102
                int xmin=block.clip.x0-block.box.x0+block.xscroll-xgap;
103
                int ymin=block.clip.y0-block.box.y1+block.yscroll+ygap;
104
                int xmax=block.clip.x1-block.box.x0+block.xscroll-xgap;
105
                int ymax=block.clip.y1-block.box.y1+block.yscroll+ygap;
106
 
107
                // Determine which cells are within clipping box.
108
                int xcmin=(xmin)/(_xcsize+xgap);
109
                int ycmin=(-ymax)/(_ycsize+ygap);
110
                int xcmax=(xmax-1)/(_xcsize+xgap)+1;
111
                int ycmax=(-ymin-1)/(_ycsize+ygap)+1;
112
                if (xcmin<0) xcmin=0;
113
                if (ycmin<0) ycmin=0;
114
                if (xcmax>_xccount) xcmax=_xccount;
115
                if (ycmax>_yccount) ycmax=_yccount;
116
 
117
                // Iterate over cells to be redrawn.
118
                for (int yc=ycmin;yc<ycmax;++yc)
119
                {
120
                        for (int xc=xcmin;xc<xcmax;++xc)
121
                        {
122
                                // Determine whether cell is occupied.
30 gdshaw@RISCPKG.ORG 123
                                unsigned int i=xc+yc*_xccount;
124
                                if (i<_directory.size())
25 gdshaw@RISCPKG.ORG 125
                                {
126
                                        // Redraw cell.
127
                                        os_box box;
128
                                        box.x0=xc*(_xcsize+xgap)+xgap;
129
                                        box.y0=-yc*(_ycsize+ygap)-_ycsize-ygap;
130
                                        box.x1=box.x0+_xcsize;
131
                                        box.y1=box.y0+_ycsize;
132
                                        layout.plot(box,*_directory[i],
133
                                                _directory[i].selected(),_options);
134
                                }
135
                        }
136
                }
137
 
21 gdshaw@RISCPKG.ORG 138
                xwimp_get_rectangle(&block,&more);
139
        }
140
}
141
 
28 gdshaw@RISCPKG.ORG 142
void filer_window::handle_open_request(wimp_open& block)
143
{
144
        int xccount=_xccount;
145
        int yccount=_yccount;
146
        reformat(block.visible.x1-block.visible.x0);
147
        xwimp_open_window(&block);
148
        if (_xccount!=xccount||_yccount!=yccount) force_redraw();
149
}
150
 
29 gdshaw@RISCPKG.ORG 151
void filer_window::handle_close_request(wimp_close& block)
152
{
153
        wimp_pointer pointer;
154
        xwimp_get_pointer_info(&pointer);
155
 
156
        if (pointer.buttons&wimp_CLICK_ADJUST)
157
        {
158
                os_coord offset;
159
                offset.x=0;
160
                offset.y=0;
161
                open_parent(offset);
162
        }
163
 
164
        delete this;
165
}
166
 
30 gdshaw@RISCPKG.ORG 167
void filer_window::handle_mouse_click(wimp_pointer& block)
168
{
169
        // Fetch layout.
170
        layout_method& layout=options().layout();
171
 
172
        // Find cell under pointer.
173
        unsigned int index=find_cell(block.pos);
174
 
31 gdshaw@RISCPKG.ORG 175
        if (block.buttons==wimp_CLICK_MENU)
30 gdshaw@RISCPKG.ORG 176
        {
31 gdshaw@RISCPKG.ORG 177
                // Any existing menu will be deleted by this action.
178
                handle_menus_deleted();
179
 
180
                // Count number of cells selected.
181
                int count=0;
182
                for (unsigned int i=0,i_end=_directory.size();i!=i_end;++i)
183
                {
184
                        if (_directory[i].selected()) ++count;
185
                }
186
 
187
                // If pointer is over a cell and nothing has yet been selected
188
                // then temporarily select the cell under the pointer.
189
                if (index!=directory::npos&&!_directory[index].selected()&&!count)
190
                {
191
                        _directory[index].selected(true);
192
                        _temp_selection=index;
193
                        force_redraw_cell(index);
194
                }
195
 
196
                // Update then open the menu.
197
                shared_menu().update(this);
198
                shared_menu().show(block);
199
        }
200
        else if (block.buttons&(wimp_SINGLE_SELECT|wimp_SINGLE_ADJUST))
201
        {
30 gdshaw@RISCPKG.ORG 202
                // If single select click then first de-select everything.
203
                if (block.buttons&wimp_SINGLE_SELECT) select_all(0);
204
 
205
                // If click within a cell then select that cell.
206
                if (index!=directory::npos)
207
                {
208
                        _directory[index].selected(!_directory[index].selected());
209
                        force_redraw_cell(index);
210
                }
211
        }
212
}
213
 
31 gdshaw@RISCPKG.ORG 214
void filer_window::handle_menus_deleted()
215
{
216
        if (_temp_selection!=directory::npos)
217
        {
218
                _directory[_temp_selection].selected(0);
219
                force_redraw_cell(_temp_selection);
220
                _temp_selection=directory::npos;
221
        }
222
}
223
 
224
filer_menu& filer_window::shared_menu() const
225
{
226
        static filer_menu* _shared_menu=0;
227
        if (!_shared_menu)
228
        {
229
                _shared_menu=new filer_menu(parent_application());
230
        }
231
        return *_shared_menu;
232
}
233
 
21 gdshaw@RISCPKG.ORG 234
void filer_window::reload()
235
{
236
        static int buffer[256];
237
        _directory.load(_pathname,buffer,sizeof(buffer));
238
}
25 gdshaw@RISCPKG.ORG 239
 
240
void filer_window::reformat()
241
{
242
        wimp_window_state wstate;
243
        get_window_state(wstate);
244
        reformat(wstate.visible.x1-wstate.visible.x0);
245
}
246
 
247
void filer_window::reformat(int xsize)
248
{
249
        // Fetch layout.
250
        layout_method& layout=options().layout();
251
        int xgap=layout.xgap();
252
        int ygap=layout.ygap();
253
 
254
        // Calculate cell size.  This must be able to accommodate
255
        // - the minimum cell size for the chosen layout, and
256
        // - the actual size occupied by each file to be listed.
257
        os_coord csize=layout.min_size(_options);
258
        for (unsigned int i=0;i!=_directory.size();++i)
259
        {
260
                os_coord size=layout.size(*_directory[i],_options);
261
                if (size.x>csize.x) csize.x=size.x;
262
                if (size.y>csize.y) csize.y=size.y;
263
        }
264
 
265
        // Apply the calculated cell size.
266
        _xcsize=csize.x;
267
        _ycsize=csize.y;
268
 
269
        // Determine number of rows and columns.
270
        _xccount=xsize/(_xcsize+xgap);
271
        if (_xccount<1) _xccount=1;
272
        _yccount=(_directory.size()+_xccount-1)/_xccount;
273
        if (_yccount<1) _yccount=1;
274
 
275
        // Calculate new extent.
276
        int extent_xsize=_xccount*(_xcsize+xgap)+xgap;
277
        int extent_ysize=_yccount*(_ycsize+ygap)+ygap;
278
 
28 gdshaw@RISCPKG.ORG 279
        // Do not force the window to become narrower than the width requested,
25 gdshaw@RISCPKG.ORG 280
        // unless there are too few directory entries to justify that width.
281
        if (extent_xsize<xsize)
282
        {
283
                extent_xsize=xsize;
284
                if (((extent_xsize+_xcsize-1)/(_xcsize+xgap))>
285
                        static_cast<int>(_directory.size()))
286
                {
287
                        extent_xsize=_directory.size()*(_xcsize+xgap)+xgap;
288
                }
289
        }
290
 
291
        // Apply the calculated extent.
292
        os_box extent;
293
        extent.x0=0;
294
        extent.y0=-extent_ysize;
295
        extent.x1=extent_xsize;
296
        extent.y1=0;
297
        set_extent(extent);
298
}
29 gdshaw@RISCPKG.ORG 299
 
30 gdshaw@RISCPKG.ORG 300
unsigned int filer_window::find_cell(const os_coord& p)
301
{
302
        layout_method& layout=options().layout();
303
        int xgap=layout.xgap();
304
        int ygap=layout.ygap();
305
 
306
        wimp_window_state state;
307
        get_window_state(state);
308
        int x=p.x-state.visible.x0+state.xscroll-xgap;
309
        int y=p.y-state.visible.y1+state.yscroll+ygap;
310
 
311
        int xc=x/(_xcsize+xgap);
312
        int yc=(-y)/(_ycsize+ygap);
313
        unsigned int index=xc+yc*_xccount;
314
 
315
        x-=xc*(_xcsize+xgap);
316
        y+=yc*(_ycsize+ygap);
317
 
318
        int found=(x>=0)&&(y>=-_ycsize)&&(x<_xcsize)&&(y<0)&&
319
                (xc>=0)&&(yc>=0)&&(xc<_xccount)&&(yc<_yccount)&&
320
                (index<_directory.size());
321
 
322
        return (found)?index:directory::npos;
323
}
324
 
325
void filer_window::force_redraw_cell(unsigned int index)
326
{
327
        layout_method& layout=options().layout();
328
        int xgap=layout.xgap();
329
        int ygap=layout.ygap();
330
 
331
        int xc=index%_xccount;
332
        int yc=index/_xccount;
333
 
334
        os_box box;
335
        box.x0=xgap+xc*(_xcsize+xgap);
336
        box.y0=-(yc+1)*(_ycsize+ygap);
337
        box.x1=box.x0+_xcsize;
338
        box.y1=box.y0+_ycsize;
339
        force_redraw(box);
340
}
341
 
342
void filer_window::select_all(int selected)
343
{
344
        layout_method& layout=options().layout();
345
        int xgap=layout.xgap();
346
        int ygap=layout.ygap();
347
 
348
        int xc_min=_xccount;
349
        int yc_min=_yccount;
350
        int xc_max=0;
351
        int yc_max=0;
352
 
353
        for (unsigned int i=0;i!=_directory.size();++i)
354
        {
355
                if (_directory[i].selected()!=selected)
356
                {
357
                        _directory[i].selected(selected);
358
 
359
                        int xc=i%_xccount;
360
                        int yc=i/_xccount;
361
                        if (xc<xc_min) xc_min=xc;
362
                        if (yc<yc_min) yc_min=yc;
363
                        if (xc+1>xc_max) xc_max=xc+1;
364
                        if (yc+1>yc_max) yc_max=yc+1;
365
                }
366
        }
367
 
368
        if ((xc_max>xc_min)&&(yc_max>yc_min))
369
        {
370
                os_box box;
371
                box.x0=xgap+xc_min*(_xcsize+xgap);
372
                box.y0=-yc_max*(_ycsize+ygap);
373
                box.x1=xc_max*(_xcsize+xgap);
374
                box.y1=-ygap-yc_min*(_ycsize+ygap);
375
                force_redraw(box);
376
        }
31 gdshaw@RISCPKG.ORG 377
 
378
        _temp_selection=directory::npos;
30 gdshaw@RISCPKG.ORG 379
}
380
 
29 gdshaw@RISCPKG.ORG 381
void filer_window::open_parent(const os_coord& offset) const
382
{
383
        // Determine whether there is a parent directory.
384
        char* lastdot=strrchr(_pathname,'.');
385
        if (lastdot&&strcmp(lastdot+1,"$"))
386
        {
387
                // Find top-left corner of this window.
388
                wimp_window_state state;
389
                get_window_state(state);
390
 
391
                // Add offset to give top-left corner of new window.
392
                os_box box;
393
                box.x0=state.visible.x0+offset.x;
394
                box.y1=state.visible.y1+offset.y;
395
                box.x1=box.x0;
396
                box.y0=box.y1;
397
 
398
                // Open new window.
399
                *lastdot=0;
400
                new filer_window((filer_application*)parent_application(),
401
                        _pathname,box,_options);
402
                *lastdot='.';
403
        }
404
}