Subversion Repositories Filer-Free

Rev

Rev 63 | 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>
64 gdshaw@RISCPKG.ORG 8
#include <new>
21 gdshaw@RISCPKG.ORG 9
 
35 gdshaw@RISCPKG.ORG 10
#include "oslib/osbyte.h"
42 gdshaw@RISCPKG.ORG 11
#include "oslib/osfscontrol.h"
35 gdshaw@RISCPKG.ORG 12
#include "oslib/wimp.h"
13
 
21 gdshaw@RISCPKG.ORG 14
#include "template_loader.h"
55 gdshaw@RISCPKG.ORG 15
#include "filer_sprite.h"
25 gdshaw@RISCPKG.ORG 16
#include "layout_method.h"
31 gdshaw@RISCPKG.ORG 17
#include "filer_menu.h"
21 gdshaw@RISCPKG.ORG 18
#include "filer_window.h"
19
#include "filer_application.h"
20
 
35 gdshaw@RISCPKG.ORG 21
namespace {
22
 
23
/** The maximum length of a leafname. */
24
const size_t max_name_length=256;
25
 
26
/** The initial horizontal offset for opening a subdirectory. */
27
const int open_xstart=20;
28
 
29
/** The initial vertical offset for opening a subdirectory. */
30
const int open_ystart=-32;
31
 
32
/** The horizontal offset between subdirectories. */
33
const int open_xstep=20;
34
 
35
/** The vertical offset between subdirectories. */
36
const int open_ystep=0;
37
 
38
/** The number of distinct subdirectory offsets. */
39
const unsigned int open_count=8;
40
 
38 gdshaw@RISCPKG.ORG 41
/** The pathname for the template file. */
42
const char* template_pathname="Resources:$.Resources.Filer.Templates";
43
 
35 gdshaw@RISCPKG.ORG 44
}; /* anonymous namespace */
45
 
21 gdshaw@RISCPKG.ORG 46
filer_window::filer_window(filer_application* app,const char* pathname,
25 gdshaw@RISCPKG.ORG 47
        const os_box& box,const filer_options& options):
21 gdshaw@RISCPKG.ORG 48
        window(app),
64 gdshaw@RISCPKG.ORG 49
        _pathname(new(std::nothrow) char[std::strlen(pathname)+1]),
25 gdshaw@RISCPKG.ORG 50
        _options(options),
51
        _directory(options.sort()),
52
        _xcsize(68),
53
        _ycsize(68),
54
        _xccount(1),
31 gdshaw@RISCPKG.ORG 55
        _yccount(1),
35 gdshaw@RISCPKG.ORG 56
        _temp_selection(directory::npos),
57
        _auto_pos(open_xstart,open_ystart,open_xstep,open_ystep,open_count)
21 gdshaw@RISCPKG.ORG 58
{
59
        // Copy pathname into buffer owned by this object.
60
        std::strcpy(_pathname,pathname);
61
 
62
        // Load directory listing.
63
        reload();
64
 
65
        // Create window from template.
38 gdshaw@RISCPKG.ORG 66
        template_loader loader("directory",template_pathname);
21 gdshaw@RISCPKG.ORG 67
        wimp_window& wblock=loader;
68
        wblock.title_flags|=wimp_ICON_INDIRECTED;
69
        wblock.title_data.indirected_text.text=_pathname;
70
        wblock.title_data.indirected_text.validation="";
71
        wblock.title_data.indirected_text.size=strlen(_pathname);
72
        wblock.visible=box;
73
        wblock.extent.x0=0;
74
        wblock.extent.y0=-(box.y1-box.y0);
75
        wblock.extent.x1=box.x1-box.x0;
76
        wblock.extent.y1=0;
28 gdshaw@RISCPKG.ORG 77
        wblock.flags|=wimp_WINDOW_IGNORE_XEXTENT;
21 gdshaw@RISCPKG.ORG 78
        wblock.icon_count=0;
79
        create_window(wblock);
80
 
81
        // Resize extent to match actual visible area.
82
        wimp_window_state wstate;
83
        get_window_state(wstate);
84
        os_box extent;
85
        extent.x0=0;
86
        extent.y0=-(wstate.visible.y1-wstate.visible.y0);
87
        extent.x1=wstate.visible.x1-wstate.visible.x0;
88
        extent.y1=0;
89
        window::set_extent(extent);
90
 
25 gdshaw@RISCPKG.ORG 91
        // Open window, but keep it hidden from view.
21 gdshaw@RISCPKG.ORG 92
        wimp_window_info winfo;
93
        get_window_info_header(winfo);
94
        wimp_open open;
95
        open.visible.x0=winfo.visible.x0;
96
        open.visible.y0=winfo.visible.y1-(winfo.extent.y1-winfo.extent.y0);
97
        open.visible.x1=winfo.visible.x0+(winfo.extent.x1-winfo.extent.x0);
98
        open.visible.y1=winfo.visible.y1;
99
        open.visible=winfo.visible;
100
        open.xscroll=0;
101
        open.yscroll=0;
25 gdshaw@RISCPKG.ORG 102
        open.next=wimp_HIDDEN;
103
        open_window(open);
104
 
105
        // Reformat content of window.
106
        reformat();
107
 
108
        // Reopen window, now making it visible.
109
        get_window_state(wstate);
110
        open.visible=wstate.visible;
111
        open.xscroll=wstate.xscroll;
112
        open.yscroll=wstate.yscroll;
21 gdshaw@RISCPKG.ORG 113
        open.next=wimp_TOP;
114
        open_window(open);
63 gdshaw@RISCPKG.ORG 115
 
116
        // Register this window in the pathnames table.
117
        if (app) _pathname_node=app->register_pathname(*this);
21 gdshaw@RISCPKG.ORG 118
}
119
 
120
filer_window::~filer_window()
121
{
63 gdshaw@RISCPKG.ORG 122
        // Deregister this window from the pathnames table.
123
        if (filer_application* app=
124
                dynamic_cast<filer_application*>(parent_application()))
125
        {
126
                app->deregister_pathname(_pathname_node);
127
        }
128
 
21 gdshaw@RISCPKG.ORG 129
        // Delete pathname buffer.
130
        delete[] _pathname;
131
}
132
 
133
void filer_window::handle_redraw_request(wimp_draw& block)
134
{
25 gdshaw@RISCPKG.ORG 135
        // Fetch layout.
136
        layout_method& layout=_options.layout();
137
        int xgap=layout.xgap();
138
        int ygap=layout.ygap();
139
 
21 gdshaw@RISCPKG.ORG 140
        int more=0;
141
        xwimp_redraw_window(&block,&more);
142
        while (more)
143
        {
25 gdshaw@RISCPKG.ORG 144
                // Get clipping coordinates with respect to this window.
145
                int xmin=block.clip.x0-block.box.x0+block.xscroll-xgap;
146
                int ymin=block.clip.y0-block.box.y1+block.yscroll+ygap;
147
                int xmax=block.clip.x1-block.box.x0+block.xscroll-xgap;
148
                int ymax=block.clip.y1-block.box.y1+block.yscroll+ygap;
149
 
150
                // Determine which cells are within clipping box.
151
                int xcmin=(xmin)/(_xcsize+xgap);
152
                int ycmin=(-ymax)/(_ycsize+ygap);
153
                int xcmax=(xmax-1)/(_xcsize+xgap)+1;
154
                int ycmax=(-ymin-1)/(_ycsize+ygap)+1;
155
                if (xcmin<0) xcmin=0;
156
                if (ycmin<0) ycmin=0;
157
                if (xcmax>_xccount) xcmax=_xccount;
158
                if (ycmax>_yccount) ycmax=_yccount;
159
 
160
                // Iterate over cells to be redrawn.
161
                for (int yc=ycmin;yc<ycmax;++yc)
162
                {
163
                        for (int xc=xcmin;xc<xcmax;++xc)
164
                        {
165
                                // Determine whether cell is occupied.
30 gdshaw@RISCPKG.ORG 166
                                unsigned int i=xc+yc*_xccount;
167
                                if (i<_directory.size())
25 gdshaw@RISCPKG.ORG 168
                                {
169
                                        // Redraw cell.
170
                                        os_box box;
171
                                        box.x0=xc*(_xcsize+xgap)+xgap;
172
                                        box.y0=-yc*(_ycsize+ygap)-_ycsize-ygap;
173
                                        box.x1=box.x0+_xcsize;
174
                                        box.y1=box.y0+_ycsize;
175
                                        layout.plot(box,*_directory[i],
176
                                                _directory[i].selected(),_options);
177
                                }
178
                        }
179
                }
180
 
21 gdshaw@RISCPKG.ORG 181
                xwimp_get_rectangle(&block,&more);
182
        }
183
}
184
 
28 gdshaw@RISCPKG.ORG 185
void filer_window::handle_open_request(wimp_open& block)
186
{
187
        int xccount=_xccount;
188
        int yccount=_yccount;
189
        reformat(block.visible.x1-block.visible.x0);
190
        xwimp_open_window(&block);
191
        if (_xccount!=xccount||_yccount!=yccount) force_redraw();
192
}
193
 
29 gdshaw@RISCPKG.ORG 194
void filer_window::handle_close_request(wimp_close& block)
195
{
196
        wimp_pointer pointer;
197
        xwimp_get_pointer_info(&pointer);
198
 
199
        if (pointer.buttons&wimp_CLICK_ADJUST)
200
        {
201
                os_coord offset;
202
                offset.x=0;
203
                offset.y=0;
204
                open_parent(offset);
205
        }
206
 
207
        delete this;
208
}
209
 
30 gdshaw@RISCPKG.ORG 210
void filer_window::handle_mouse_click(wimp_pointer& block)
211
{
212
        // Fetch layout.
213
        layout_method& layout=options().layout();
214
 
35 gdshaw@RISCPKG.ORG 215
        // Read shift state.
216
        int shift_state=0;
217
        xosbyte1(osbyte_VAR_KEYBOARD_STATE,0,0xff,&shift_state);
58 gdshaw@RISCPKG.ORG 218
        bool alt=(shift_state&0x01)!=0;
219
        bool shift=(shift_state&0x08)!=0;
35 gdshaw@RISCPKG.ORG 220
 
30 gdshaw@RISCPKG.ORG 221
        // Find cell under pointer.
222
        unsigned int index=find_cell(block.pos);
223
 
35 gdshaw@RISCPKG.ORG 224
        bool close=false;
31 gdshaw@RISCPKG.ORG 225
        if (block.buttons==wimp_CLICK_MENU)
30 gdshaw@RISCPKG.ORG 226
        {
31 gdshaw@RISCPKG.ORG 227
                // Any existing menu will be deleted by this action.
228
                handle_menus_deleted();
229
 
230
                // Count number of cells selected.
231
                int count=0;
232
                for (unsigned int i=0,i_end=_directory.size();i!=i_end;++i)
233
                {
234
                        if (_directory[i].selected()) ++count;
235
                }
236
 
237
                // If pointer is over a cell and nothing has yet been selected
238
                // then temporarily select the cell under the pointer.
239
                if (index!=directory::npos&&!_directory[index].selected()&&!count)
240
                {
241
                        _directory[index].selected(true);
242
                        _temp_selection=index;
243
                        force_redraw_cell(index);
244
                }
245
 
246
                // Update then open the menu.
247
                shared_menu().update(this);
248
                shared_menu().show(block);
249
        }
35 gdshaw@RISCPKG.ORG 250
        else if (block.buttons&(wimp_DOUBLE_ADJUST|wimp_DOUBLE_SELECT))
251
        {
252
                // If double click within a cell then run that object.
253
                if (index!=directory::npos)
254
                {
255
                        select_all(0);
256
                        close=(block.buttons&wimp_DOUBLE_ADJUST)!=0;
257
                        if (close) close_window();
258
                        run(*_directory[index],close,shift);
259
                }
260
        }
55 gdshaw@RISCPKG.ORG 261
        else if (block.buttons&(wimp_DRAG_SELECT|wimp_DRAG_ADJUST))
262
        {
263
                // No action unless the pointer is over a cell.
264
                if (index!=directory::npos)
265
                {
58 gdshaw@RISCPKG.ORG 266
                        // No action if select drag with alt.
267
                        if ((block.buttons&wimp_DRAG_ADJUST)||!alt)
268
                        {
269
                                // Choose sprite to be dragged.
270
                                filer_sprite sprite(*_directory[index]);
55 gdshaw@RISCPKG.ORG 271
 
58 gdshaw@RISCPKG.ORG 272
                                // Begin the drag with this sprite.
273
                                begin_drag(block,sprite.name());
274
                        }
55 gdshaw@RISCPKG.ORG 275
                }
276
        }
57 gdshaw@RISCPKG.ORG 277
        else if (block.buttons&wimp_SINGLE_ADJUST)
31 gdshaw@RISCPKG.ORG 278
        {
58 gdshaw@RISCPKG.ORG 279
                // Cancel rename if one is in progress.
280
                if (filer_application* app=
281
                        dynamic_cast<filer_application*>(parent_application()))
282
                {
283
                        app->cancel_rename();
284
                }
285
 
57 gdshaw@RISCPKG.ORG 286
                // Invert selection state of cell under pointer.
30 gdshaw@RISCPKG.ORG 287
                if (index!=directory::npos)
288
                {
289
                        _directory[index].selected(!_directory[index].selected());
290
                        force_redraw_cell(index);
291
                }
292
        }
57 gdshaw@RISCPKG.ORG 293
        else if (block.buttons&wimp_SINGLE_SELECT)
294
        {
58 gdshaw@RISCPKG.ORG 295
                // Cancel rename if one is in progress.
296
                if (filer_application* app=
297
                        dynamic_cast<filer_application*>(parent_application()))
57 gdshaw@RISCPKG.ORG 298
                {
58 gdshaw@RISCPKG.ORG 299
                        app->cancel_rename();
300
                }
301
 
302
                if (alt&&index!=directory::npos)
303
                {
304
                        if (filer_application* app=dynamic_cast<filer_application*>(
305
                                parent_application()))
306
                        {
307
                                app->begin_rename(*this,index,block.pos);
308
                        }
309
                }
310
                else if (index==directory::npos||!_directory[index].selected())
311
                {
312
                        // Select click over a cell which is not already selected.
57 gdshaw@RISCPKG.ORG 313
                        // De-select everything.
314
                        select_all(0);
35 gdshaw@RISCPKG.ORG 315
 
57 gdshaw@RISCPKG.ORG 316
                        // Select cell under pointer.
317
                        if (index!=directory::npos)
318
                        {
319
                                _directory[index].selected(true);
320
                                force_redraw_cell(index);
321
                        }
322
                }
323
        }
324
 
35 gdshaw@RISCPKG.ORG 325
        // If this window has closed as a result of the mouse event
326
        // then delete it.
327
        if (close) delete this;
30 gdshaw@RISCPKG.ORG 328
}
329
 
55 gdshaw@RISCPKG.ORG 330
void filer_window::handle_user_drag_box(wimp_dragged& block)
331
{
332
        // Read pointer state.
333
        wimp_pointer pointer;
334
        xwimp_get_pointer_info(&pointer);
335
 
336
        // Read shift state.
337
        int shift_state=0;
338
        xosbyte1(osbyte_VAR_KEYBOARD_STATE,0,0xff,&shift_state);
339
        int shift=(shift_state&0x08)!=0;
340
 
341
        if (application* app=parent_application())
342
        {
343
                if (pointer.w==handle())
344
                {
345
                        // Object has been dropped back onto this window: no action.
346
                }
347
                else if (filer_window* w=
348
                        dynamic_cast<filer_window*>(app->find_window(pointer.w)))
349
                {
350
                        // Object has been dropped onto another filer window.
351
                        wimp_t filer_action_handle=begin_filer_action();
352
 
353
                        // Copy or move according to shift state.
354
                        const char* dst_pathname=w->pathname();
355
                        fileraction_flags flags=options().flags()|fileraction_RECURSE;
356
                        if (shift)
357
                        {
358
                                fileractionsendstartoperation_move(filer_action_handle,flags,
359
                                        dst_pathname,strlen(dst_pathname)+1);
360
                        }
361
                        else
362
                        {
363
                                fileractionsendstartoperation_copy(filer_action_handle,flags,
364
                                        dst_pathname,strlen(dst_pathname)+1);
365
                        }
366
                }
367
                else
368
                {
369
                        // Object has been dropped onto some other type of window.
370
                        // Construct Message_DataLoad.
371
                        wimp_message block2;
372
                        block2.your_ref=0;
373
                        block2.action=message_DATA_LOAD;
374
                        block2.data.data_xfer.w=pointer.w;
375
                        block2.data.data_xfer.i=pointer.i;
376
                        block2.data.data_xfer.pos=pointer.pos;
377
                        block2.data.data_xfer.est_size=-1; /* can do better than this */
378
                        block2.data.data_xfer.file_type=0xfff; /* can better this too */
379
 
380
                        // Send a Message_DataLoad for each object in the selection.
381
                        int count=0;
382
                        for (unsigned int i=0,i_end=_directory.size();i!=i_end;++i)
383
                        {
384
                                if (_directory[i].selected())
385
                                {
386
                                        ++count;
387
 
388
                                        sprintf(block2.data.data_xfer.file_name,"%s.%s",_pathname,
389
                                                _directory[i]->name);
390
                                        block2.size=(44+strlen(block2.data.data_xfer.file_name)+4)&~3;
391
 
392
                                        wimp_send_message_to_window(wimp_USER_MESSAGE,&block2,
393
                                                pointer.w,pointer.i);
394
                                }
395
                        }
396
                }
397
        }
398
}
399
 
58 gdshaw@RISCPKG.ORG 400
void filer_window::handle_key_pressed(wimp_key& block)
401
{
402
        parent_application()->handle_key_pressed(block);
403
}
404
 
56 gdshaw@RISCPKG.ORG 405
void filer_window::handle_data_xfer(wimp_message& block)
406
{
407
        // A buffer to temporarily hold the leafname.
408
        static char leafname[212];
409
 
410
        switch (block.action)
411
        {
412
        case message_DATA_SAVE:
413
                {
414
                        // Extract leafname.
415
                        char* p=std::strrchr(block.data.data_xfer.file_name,'.');
416
                        if (p) ++p;
417
                        else p=block.data.data_xfer.file_name;
418
                        std::strcpy(leafname,p);
419
 
420
                        // Construct and send acknowledgement.
421
                        block.your_ref=block.my_ref;
422
                        block.action=message_DATA_SAVE_ACK;
423
                        std::sprintf(block.data.data_xfer.file_name,"%s.%s",
424
                                _pathname,leafname);
425
                        block.size=(44+std::strlen(block.data.data_xfer.file_name)+4)&~3;
426
                        xwimp_send_message(wimp_USER_MESSAGE,&block,block.sender);
427
                }
428
                break;
429
        case message_DATA_LOAD:
430
                {
431
                        // Construct and send acknowledgement.
432
                        block.your_ref=block.my_ref;
433
                        block.action=message_DATA_LOAD_ACK;
434
                        xwimp_send_message(wimp_USER_MESSAGE,&block,block.sender);
435
                }
436
                break;
437
        }
438
}
439
 
31 gdshaw@RISCPKG.ORG 440
void filer_window::handle_menus_deleted()
441
{
442
        if (_temp_selection!=directory::npos)
443
        {
444
                _directory[_temp_selection].selected(0);
445
                force_redraw_cell(_temp_selection);
446
                _temp_selection=directory::npos;
447
        }
448
}
449
 
36 gdshaw@RISCPKG.ORG 450
void filer_window::options(const filer_options& options)
451
{
452
        // Keep track of whether window needs to be reformatted, restored
453
        // and redrawn.
454
        int need_reformat=0;
455
        int need_resort=0;
456
        int need_redraw=0;
457
 
458
        // Check whether layout method has changed.
459
        if (&options.layout()!=&_options.layout())
460
        {
461
                need_reformat=1;
462
                need_redraw=1;
463
        }
464
 
465
        // Check whether sorting method has changed.
466
        if (&options.sort()!=&_options.sort())
467
        {
468
                need_resort=1;
469
                need_redraw=1;
470
        }
471
 
472
        // Update options.
473
        _options=options;
474
 
475
        // Reformat, resort and redraw window as required.
476
        if (need_resort) _directory.sort(_options.sort());
477
        if (need_redraw) force_redraw();
478
        if (need_reformat)
479
        {
480
                reformat();
481
                if (need_redraw) force_redraw();
482
        }
483
}
484
 
31 gdshaw@RISCPKG.ORG 485
filer_menu& filer_window::shared_menu() const
486
{
487
        static filer_menu* _shared_menu=0;
488
        if (!_shared_menu)
489
        {
38 gdshaw@RISCPKG.ORG 490
                wimp_open_template(template_pathname);
64 gdshaw@RISCPKG.ORG 491
                _shared_menu=new(std::nothrow) filer_menu(parent_application());
38 gdshaw@RISCPKG.ORG 492
                wimp_close_template();
31 gdshaw@RISCPKG.ORG 493
        }
494
        return *_shared_menu;
495
}
496
 
21 gdshaw@RISCPKG.ORG 497
void filer_window::reload()
498
{
499
        static int buffer[256];
500
        _directory.load(_pathname,buffer,sizeof(buffer));
501
}
25 gdshaw@RISCPKG.ORG 502
 
503
void filer_window::reformat()
504
{
505
        wimp_window_state wstate;
506
        get_window_state(wstate);
507
        reformat(wstate.visible.x1-wstate.visible.x0);
508
}
509
 
510
void filer_window::reformat(int xsize)
511
{
512
        // Fetch layout.
513
        layout_method& layout=options().layout();
514
        int xgap=layout.xgap();
515
        int ygap=layout.ygap();
516
 
53 gdshaw@RISCPKG.ORG 517
        // Window will automatically resize if visible area matches extent.
518
        wimp_window_info winfo;
519
        get_window_info_header(winfo);
520
        int xauto=(winfo.xscroll==0)&&
521
                (winfo.visible.x1-winfo.visible.x0==winfo.extent.x1-winfo.extent.x0);
522
        int yauto=(winfo.yscroll==0)&&
523
                (winfo.visible.y1-winfo.visible.y0==winfo.extent.y1-winfo.extent.y0);
524
 
25 gdshaw@RISCPKG.ORG 525
        // Calculate cell size.  This must be able to accommodate
526
        // - the minimum cell size for the chosen layout, and
527
        // - the actual size occupied by each file to be listed.
528
        os_coord csize=layout.min_size(_options);
529
        for (unsigned int i=0;i!=_directory.size();++i)
530
        {
531
                os_coord size=layout.size(*_directory[i],_options);
532
                if (size.x>csize.x) csize.x=size.x;
533
                if (size.y>csize.y) csize.y=size.y;
534
        }
535
 
536
        // Apply the calculated cell size.
537
        _xcsize=csize.x;
538
        _ycsize=csize.y;
539
 
540
        // Determine number of rows and columns.
53 gdshaw@RISCPKG.ORG 541
        if (xauto&&(_yccount<2))
542
        {
543
                _xccount=(_directory.size()<4)?_directory.size():4;
544
        }
545
        else
546
        {
547
                _xccount=xsize/(_xcsize+xgap);
548
        }
25 gdshaw@RISCPKG.ORG 549
        if (_xccount<1) _xccount=1;
550
        _yccount=(_directory.size()+_xccount-1)/_xccount;
551
        if (_yccount<1) _yccount=1;
552
 
553
        // Calculate new extent.
554
        int extent_xsize=_xccount*(_xcsize+xgap)+xgap;
555
        int extent_ysize=_yccount*(_ycsize+ygap)+ygap;
556
 
28 gdshaw@RISCPKG.ORG 557
        // Do not force the window to become narrower than the width requested,
25 gdshaw@RISCPKG.ORG 558
        // unless there are too few directory entries to justify that width.
559
        if (extent_xsize<xsize)
560
        {
561
                extent_xsize=xsize;
562
                if (((extent_xsize+_xcsize-1)/(_xcsize+xgap))>
563
                        static_cast<int>(_directory.size()))
564
                {
565
                        extent_xsize=_directory.size()*(_xcsize+xgap)+xgap;
566
                }
567
        }
568
 
569
        // Apply the calculated extent.
570
        os_box extent;
571
        extent.x0=0;
572
        extent.y0=-extent_ysize;
573
        extent.x1=extent_xsize;
574
        extent.y1=0;
575
        set_extent(extent);
576
}
29 gdshaw@RISCPKG.ORG 577
 
53 gdshaw@RISCPKG.ORG 578
void filer_window::set_extent(const os_box& extent)
579
{
580
        // Window will automatically resize if visible area matches extent.
581
        wimp_window_info winfo;
582
        get_window_info_header(winfo);
583
        int xauto=(winfo.xscroll==0)&&
584
                (winfo.visible.x1-winfo.visible.x0==winfo.extent.x1-winfo.extent.x0);
585
        int yauto=(winfo.yscroll==0)&&
586
                (winfo.visible.y1-winfo.visible.y0==winfo.extent.y1-winfo.extent.y0);
587
 
588
        // Set the new extent.
589
        window::set_extent(extent);
590
 
591
        // Open the window.
592
        wimp_open open;
593
        open.visible=winfo.visible;
594
        open.xscroll=winfo.xscroll;
595
        open.yscroll=winfo.yscroll;
596
        open.next=winfo.next;
597
 
598
        // If automatically resizing then adjust visible area to match extent.
599
        int reopen=0;
600
        if (xauto&&(winfo.visible.x1-winfo.visible.x0!=extent.x1-extent.x0))
601
        {
602
                open.visible.x1=open.visible.x0+(extent.x1-extent.x0);
603
                reopen=1;
604
        }
605
        if (yauto&&(open.visible.y1-open.visible.y0!=extent.y1-extent.y0))
606
        {
607
                open.visible.y0=open.visible.y1-(extent.y1-extent.y0);
608
                reopen=1;
609
        }
610
        if (reopen) open_window(open);
611
}
612
 
30 gdshaw@RISCPKG.ORG 613
unsigned int filer_window::find_cell(const os_coord& p)
614
{
615
        layout_method& layout=options().layout();
616
        int xgap=layout.xgap();
617
        int ygap=layout.ygap();
618
 
619
        wimp_window_state state;
620
        get_window_state(state);
621
        int x=p.x-state.visible.x0+state.xscroll-xgap;
622
        int y=p.y-state.visible.y1+state.yscroll+ygap;
623
 
624
        int xc=x/(_xcsize+xgap);
625
        int yc=(-y)/(_ycsize+ygap);
626
        unsigned int index=xc+yc*_xccount;
627
 
628
        x-=xc*(_xcsize+xgap);
629
        y+=yc*(_ycsize+ygap);
630
 
631
        int found=(x>=0)&&(y>=-_ycsize)&&(x<_xcsize)&&(y<0)&&
632
                (xc>=0)&&(yc>=0)&&(xc<_xccount)&&(yc<_yccount)&&
633
                (index<_directory.size());
634
 
635
        return (found)?index:directory::npos;
636
}
637
 
638
void filer_window::force_redraw_cell(unsigned int index)
639
{
640
        layout_method& layout=options().layout();
641
        int xgap=layout.xgap();
642
        int ygap=layout.ygap();
643
 
644
        int xc=index%_xccount;
645
        int yc=index/_xccount;
646
 
647
        os_box box;
648
        box.x0=xgap+xc*(_xcsize+xgap);
58 gdshaw@RISCPKG.ORG 649
        box.y0=-(yc+1)*(_ycsize+ygap)-ygap;
30 gdshaw@RISCPKG.ORG 650
        box.x1=box.x0+_xcsize;
58 gdshaw@RISCPKG.ORG 651
        box.y1=box.y0+_ycsize+ygap;
30 gdshaw@RISCPKG.ORG 652
        force_redraw(box);
653
}
654
 
34 gdshaw@RISCPKG.ORG 655
void filer_window::refresh()
656
{
657
        reload();
658
        reformat();
659
        force_redraw();
660
}
661
 
30 gdshaw@RISCPKG.ORG 662
void filer_window::select_all(int selected)
663
{
664
        layout_method& layout=options().layout();
665
        int xgap=layout.xgap();
666
        int ygap=layout.ygap();
667
 
668
        int xc_min=_xccount;
669
        int yc_min=_yccount;
670
        int xc_max=0;
671
        int yc_max=0;
672
 
673
        for (unsigned int i=0;i!=_directory.size();++i)
674
        {
675
                if (_directory[i].selected()!=selected)
676
                {
677
                        _directory[i].selected(selected);
678
 
679
                        int xc=i%_xccount;
680
                        int yc=i/_xccount;
681
                        if (xc<xc_min) xc_min=xc;
682
                        if (yc<yc_min) yc_min=yc;
683
                        if (xc+1>xc_max) xc_max=xc+1;
684
                        if (yc+1>yc_max) yc_max=yc+1;
685
                }
686
        }
687
 
688
        if ((xc_max>xc_min)&&(yc_max>yc_min))
689
        {
690
                os_box box;
691
                box.x0=xgap+xc_min*(_xcsize+xgap);
692
                box.y0=-yc_max*(_ycsize+ygap);
693
                box.x1=xc_max*(_xcsize+xgap);
694
                box.y1=-ygap-yc_min*(_ycsize+ygap);
695
                force_redraw(box);
696
        }
31 gdshaw@RISCPKG.ORG 697
 
698
        _temp_selection=directory::npos;
30 gdshaw@RISCPKG.ORG 699
}
700
 
58 gdshaw@RISCPKG.ORG 701
wimp_i filer_window::open_rename(directory::size_type index,os_coord pos,
702
        char* buffer)
703
{
704
        // Fetch layout.
705
        layout_method& layout=_options.layout();
706
        int xgap=layout.xgap();
707
        int ygap=layout.ygap();
708
 
709
        // Calculate cell coordinates from cell index.
710
        int xc=index%_xccount;
711
        int yc=index/_xccount;
712
        os_box box;
713
        box.x0=xc*(_xcsize+xgap)+xgap;
714
        box.y0=-yc*(_ycsize+ygap)-_ycsize-ygap;
715
        box.x1=box.x0+_xcsize;
716
        box.y1=box.y0+_ycsize;
717
 
718
        // Construct icon block for name field.
719
        wimp_icon_create icon_c;
720
        icon_c.w=handle();
721
        icon_c.icon=*layout.icon_data(layout_method::icon_name,box,
722
                *_directory[index],false,_options);
723
 
724
        // Extend field upwards and downwards by 4 OS units,
725
        // and sideways to fill cell.
726
        icon_c.icon.extent.x0=box.x0;
727
        icon_c.icon.extent.y0-=4;
728
        icon_c.icon.extent.x1=box.x1;
729
        icon_c.icon.extent.y1+=4;
730
 
731
        // Modify icon flags to make it a writable field.
732
        icon_c.icon.flags&=~(wimp_ICON_BUTTON_TYPE|wimp_ICON_BG_COLOUR);
733
        icon_c.icon.flags|=wimp_ICON_BORDER|wimp_ICON_FILLED;
734
        icon_c.icon.flags|=wimp_BUTTON_WRITABLE<<wimp_ICON_BUTTON_TYPE_SHIFT;
735
        icon_c.icon.flags|=wimp_COLOUR_WHITE<<wimp_ICON_BG_COLOUR_SHIFT;
736
 
737
        // Switch to using dedicated buffer.
738
        // (The one returned by the layout class is neither large enough
739
        // nor sufficiently persistant.)
740
        std::strcpy(buffer,icon_c.icon.data.indirected_text.text);
741
        icon_c.icon.data.indirected_text.text=buffer;
742
        icon_c.icon.data.indirected_text.size=256;
743
 
744
        // Make pointer coordinates relative to work area.
745
        wimp_window_state wstate;
746
        wstate.w=handle();
747
        wimp_get_window_state(&wstate);
748
        os_coord rel_pos;
749
        rel_pos.x=pos.x-wstate.visible.x0+wstate.xscroll;
750
        rel_pos.y=pos.y-wstate.visible.y1+wstate.yscroll;
751
 
752
        // Create the icon, claiming the caret.
753
        wimp_i ic=wimp_create_icon(&icon_c);
754
        force_redraw_cell(index);
755
        wimp_set_caret_position(handle(),ic,rel_pos.x,rel_pos.y,-1,-1);
756
 
757
        // Return the icon handle.
758
        return ic;
759
}
760
 
761
void filer_window::close_rename(wimp_i ic,directory::size_type index)
762
{
763
        wimp_delete_icon(handle(),ic);
764
        force_redraw_cell(index);
765
}
766
 
767
void filer_window::rename(directory::size_type index,const char* leafname)
768
{
769
        osgbpb_info* object=_directory[index];
770
        if (object&&std::strlen(leafname))
771
        {
772
                const size_t max_pathname=255;
773
                size_t max_leafname=
774
                        max_pathname-std::strlen(pathname())-1;
775
                if (max_leafname>max_pathname) max_leafname=0;
776
                if ((std::strlen(object->name)<=max_leafname)&&
777
                        (std::strlen(leafname)<=max_leafname))
778
                {
779
                        static char src_pathname[max_pathname+1];
780
                        static char dst_pathname[max_pathname+1];
781
                        std::sprintf(src_pathname,"%s.%s",pathname(),object->name);
782
                        std::sprintf(dst_pathname,"%s.%s",pathname(),leafname);
783
                        osfscontrol_rename(src_pathname,dst_pathname);
784
                }
785
        }
786
}
787
 
29 gdshaw@RISCPKG.ORG 788
void filer_window::open_parent(const os_coord& offset) const
789
{
790
        // Determine whether there is a parent directory.
791
        char* lastdot=strrchr(_pathname,'.');
792
        if (lastdot&&strcmp(lastdot+1,"$"))
793
        {
794
                // Find top-left corner of this window.
795
                wimp_window_state state;
796
                get_window_state(state);
797
 
798
                // Add offset to give top-left corner of new window.
799
                os_box box;
800
                box.x0=state.visible.x0+offset.x;
801
                box.y1=state.visible.y1+offset.y;
802
                box.x1=box.x0;
803
                box.y0=box.y1;
804
 
805
                // Open new window.
806
                *lastdot=0;
64 gdshaw@RISCPKG.ORG 807
                new(std::nothrow) filer_window(
808
                        (filer_application*)parent_application(),_pathname,box,_options);
29 gdshaw@RISCPKG.ORG 809
                *lastdot='.';
810
        }
811
}
35 gdshaw@RISCPKG.ORG 812
 
42 gdshaw@RISCPKG.ORG 813
void filer_window::set_work_directory() const
814
{
815
        osfscontrol_dir(_pathname);
816
}
817
 
45 gdshaw@RISCPKG.ORG 818
wimp_t filer_window::begin_filer_action()
819
{
820
        // Start a FilerAction task.
821
        wimp_t handle=wimp_start_task("Filer_Action");
822
 
823
        // Send it the pathname of the directory on which it is to act.
824
        fileraction_send_selected_directory(handle,pathname());
825
 
826
        // Send it the list of leafnames on which it is to act.
827
        for (unsigned int i=0,i_end=_directory.size();i!=i_end;++i)
828
        {
829
                if (_directory[i].selected())
830
                {
831
                        const char* leafname=_directory[i]->name;
832
                        fileraction_send_selected_file(handle,leafname);
833
                }
834
        }
835
 
836
        // Return the task handle of the FilerAction task.
837
        return handle;
838
}
839
 
35 gdshaw@RISCPKG.ORG 840
void filer_window::run(osgbpb_info& info,int close,int shift)
841
{
842
        // Need to at least partially handle shift key here, because
843
        // Filer_Run does not allow coordinates to be specified when
844
        // shift-clicking an application directory.
845
        // (... unless there is an alternative way to invoke Filer_Run?)
846
 
847
        static char buffer[12+max_name_length];
848
        if ((info.obj_type&fileswitch_IS_DIR)&&((info.name[0]!='!')||shift))
849
        {
850
                // Get dimensions of this window.
851
                wimp_window_state state;
852
                get_window_state(state);
853
 
854
                // Choose coordinates for new window.
855
                // If closing this window then open in same position,
856
                // otherwise open below and to the right.
857
                os_coord offset=_auto_pos();
858
                os_box box;
859
                box.x0=state.visible.x0+((close)?0:offset.x);
860
                box.y1=state.visible.y1+((close)?0:offset.y);
861
                box.x1=box.x0;
862
                box.y0=box.y1;
863
 
864
                // Open new window.
865
                if (std::strlen(_pathname)+std::strlen(info.name)+1<max_name_length)
866
                {
867
                        std::sprintf(buffer,"%s.%s",_pathname,info.name);
64 gdshaw@RISCPKG.ORG 868
                        window* w=new(std::nothrow) filer_window(
35 gdshaw@RISCPKG.ORG 869
                                (filer_application*)parent_application(),
870
                                buffer,box,_options);
871
                }
872
        }
873
        else
874
        {
875
                // Run object.
876
                if (std::strlen(_pathname)+std::strlen(info.name)+1<max_name_length)
877
                {
878
                        std::sprintf(buffer,"Run %s.%s",_pathname,info.name);
879
                        wimp_start_task(buffer);
880
                }
881
        }
882
}