~ubuntu-branches/ubuntu/precise/fluxbox/precise

« back to all changes in this revision

Viewing changes to util/fbrun/FbRun.cc

  • Committer: Bazaar Package Importer
  • Author(s): Dmitry E. Oboukhov
  • Date: 2008-07-01 10:38:14 UTC
  • mfrom: (2.1.12 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080701103814-khx2b6il152x9p93
Tags: 1.0.0+deb1-8
* x-dev has been removed from build-depends (out-of-date package).
* Standards-Version bumped to 3.8.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// FbRun.cc
2
 
// Copyright (c) 2002 - 2005 Henrik Kinnunen (fluxgen at fluxbox dot org)
3
 
//
4
 
// Permission is hereby granted, free of charge, to any person obtaining a
5
 
// copy of this software and associated documentation files (the "Software"),
6
 
// to deal in the Software without restriction, including without limitation
7
 
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
 
// and/or sell copies of the Software, and to permit persons to whom the
9
 
// Software is furnished to do so, subject to the following conditions:
10
 
//
11
 
// The above copyright notice and this permission notice shall be included in
12
 
// all copies or substantial portions of the Software.
13
 
//
14
 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17
 
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20
 
// DEALINGS IN THE SOFTWARE.
21
 
 
22
 
// $Id: FbRun.cc 4001 2005-05-09 07:20:17Z mathias $
23
 
 
24
 
#include "FbRun.hh"
25
 
 
26
 
#include "App.hh"
27
 
#include "EventManager.hh"
28
 
#include "Color.hh"
29
 
#include "KeyUtil.hh"
30
 
#include "FileUtil.hh"
31
 
 
32
 
#ifdef HAVE_CONFIG_H
33
 
#include "config.h"
34
 
#endif // HAVE_CONFIG_H
35
 
 
36
 
#ifdef HAVE_XPM
37
 
#include <X11/xpm.h>
38
 
#include "fbrun.xpm"
39
 
#endif // HAVE_XPM
40
 
 
41
 
#include <X11/Xlib.h>
42
 
#include <X11/keysym.h>
43
 
#include <X11/Xutil.h>
44
 
#include <X11/cursorfont.h>
45
 
#include <unistd.h>
46
 
 
47
 
#include <iostream>
48
 
#include <iterator>
49
 
#include <fstream>
50
 
#include <algorithm>
51
 
#ifdef HAVE_CASSERT
52
 
  #include <cassert>
53
 
#else
54
 
  #include <assert.h>
55
 
#endif
56
 
 
57
 
using namespace std;
58
 
FbRun::FbRun(int x, int y, size_t width):
59
 
    FbTk::TextBox(DefaultScreen(FbTk::App::instance()->display()),
60
 
                  m_font, ""),
61
 
    m_font("fixed"),
62
 
    m_display(FbTk::App::instance()->display()),
63
 
    m_bevel(4),
64
 
    m_gc(*this),
65
 
    m_end(false),
66
 
    m_current_history_item(0),
67
 
    m_current_apps_item(0),
68
 
    m_cursor(XCreateFontCursor(FbTk::App::instance()->display(), XC_xterm)) {
69
 
    
70
 
    setGC(m_gc.gc());
71
 
    setCursor(m_cursor);
72
 
    // setting nomaximize in local resize
73
 
    resize(width, font().height() + m_bevel);
74
 
 
75
 
    // setup class name
76
 
    XClassHint *class_hint = XAllocClassHint();
77
 
    if (class_hint == 0)
78
 
        throw string("Out of memory");
79
 
    class_hint->res_name = "fbrun";
80
 
    class_hint->res_class = "FbRun";    
81
 
    XSetClassHint(m_display, window(), class_hint);
82
 
    
83
 
    XFree(class_hint);
84
 
#ifdef HAVE_XPM
85
 
    Pixmap mask = 0;
86
 
    Pixmap pm;
87
 
    XpmCreatePixmapFromData(m_display,
88
 
                            window(),
89
 
                            fbrun_xpm,
90
 
                            &pm,
91
 
                            &mask,
92
 
                            0); // attribs
93
 
    if (mask != 0)
94
 
        XFreePixmap(m_display, mask);
95
 
 
96
 
    m_pixmap = pm;
97
 
#endif // HAVE_XPM
98
 
 
99
 
    if (m_pixmap.drawable()) {
100
 
        XWMHints wmhints;
101
 
        wmhints.flags = IconPixmapHint;
102
 
        wmhints.icon_pixmap = m_pixmap.drawable();
103
 
        XSetWMHints(m_display, window(), &wmhints);
104
 
    }
105
 
 
106
 
}
107
 
 
108
 
 
109
 
FbRun::~FbRun() {
110
 
    hide();
111
 
}
112
 
 
113
 
void FbRun::run(const std::string &command) {
114
 
    FbTk::App::instance()->end(); // end application
115
 
    m_end = true; // mark end of processing
116
 
 
117
 
    // fork and execute program
118
 
    if (!fork()) {
119
 
        setsid();
120
 
        execl("/bin/sh", "/bin/sh", "-c", command.c_str(), static_cast<void*>(NULL));
121
 
        exit(0); //exit child
122
 
    }
123
 
 
124
 
    hide(); // hide gui
125
 
    
126
 
    // save command history to file
127
 
    if (text().size() != 0) { // no need to save empty command
128
 
 
129
 
        // don't allow duplicates into the history file, first
130
 
        // look for a duplicate
131
 
        if (m_current_history_item < m_history.size()
132
 
            && text() == m_history[m_current_history_item]) {
133
 
            // m_current_history_item is the duplicate
134
 
        } else {
135
 
            m_current_history_item = 0;
136
 
            for (; m_current_history_item < m_history.size(); 
137
 
                 ++m_current_history_item) {
138
 
                if (m_history[m_current_history_item] == text())
139
 
                    break;
140
 
            }
141
 
        }
142
 
 
143
 
        fstream inoutfile(m_history_file.c_str(), ios::in|ios::out);
144
 
        if (inoutfile) {
145
 
            // now m_current_history_item points at the duplicate, or
146
 
            // at m_history.size() if no duplicate
147
 
            if (m_current_history_item != m_history.size()) {
148
 
                int i = 0;
149
 
                // read past history items before current
150
 
                for (; inoutfile.good() && i < m_current_history_item; i++)
151
 
                    inoutfile.ignore(1, '\n');
152
 
 
153
 
                // write the history items that come after current
154
 
                for (i++; i < m_history.size(); i++)
155
 
                    inoutfile<<m_history[i]<<endl;
156
 
                
157
 
            } else {
158
 
                // set put-pointer at end of file
159
 
                inoutfile.seekp(0, ios::end);
160
 
            }
161
 
            // append current command to the file
162
 
            inoutfile<<command<<endl;
163
 
 
164
 
        } else
165
 
            cerr<<"FbRun Warning: Can't write command history to file: "<<m_history_file<<endl;
166
 
    }
167
 
 
168
 
}
169
 
 
170
 
bool FbRun::loadHistory(const char *filename) {
171
 
    if (filename == 0)
172
 
        return false;
173
 
    ifstream infile(filename);
174
 
    if (!infile) {
175
 
        //even though we fail to load file, we should try save to it
176
 
        ofstream outfile(filename);
177
 
        if (outfile) {
178
 
            m_history_file = filename;
179
 
            return true;
180
 
        }
181
 
        return false;
182
 
    }
183
 
    // clear old history and load new one from file
184
 
    m_history.clear();
185
 
    // each line is a command
186
 
    string line;
187
 
    while (!infile.eof()) {
188
 
        getline(infile, line);
189
 
        if (line.size()) // don't add empty lines
190
 
            m_history.push_back(line);
191
 
    }
192
 
    // set no current histor to display
193
 
    m_current_history_item = m_history.size();
194
 
    // set history file
195
 
    m_history_file = filename;
196
 
    return true;
197
 
}
198
 
 
199
 
bool FbRun::loadFont(const string &fontname) {
200
 
    if (!m_font.load(fontname.c_str()))
201
 
        return false;
202
 
 
203
 
    // resize to fit new font height
204
 
    resize(width(), font().height() + m_bevel);
205
 
    return true;
206
 
}
207
 
 
208
 
void FbRun::setForegroundColor(const FbTk::Color &color) {
209
 
    m_gc.setForeground(color);
210
 
}
211
 
 
212
 
void FbRun::setTitle(const string &title) {
213
 
    setName(title.c_str());
214
 
}
215
 
 
216
 
void FbRun::resize(unsigned int width, unsigned int height) {
217
 
    FbTk::TextBox::resize(width, height);    
218
 
    setNoMaximize();
219
 
}
220
 
 
221
 
void FbRun::redrawLabel() {
222
 
    clear();
223
 
}
224
 
 
225
 
void FbRun::keyPressEvent(XKeyEvent &ke) {
226
 
    
227
 
    ke.state = FbTk::KeyUtil::instance().cleanMods(ke.state);
228
 
 
229
 
    int cp= cursorPosition();
230
 
    FbTk::TextBox::keyPressEvent(ke);
231
 
    KeySym ks;
232
 
    char keychar[1];
233
 
    XLookupString(&ke, keychar, 1, &ks, 0);
234
 
    // a modifier key by itself doesn't do anything
235
 
    if (IsModifierKey(ks)) 
236
 
        return;
237
 
 
238
 
    if (FbTk::KeyUtil::instance().isolateModifierMask(ke.state)) { // a modifier key is down
239
 
        if ((ke.state & ControlMask) == ControlMask) {
240
 
            switch (ks) {
241
 
            case XK_p:
242
 
                prevHistoryItem();
243
 
                break;
244
 
            case XK_n:
245
 
                nextHistoryItem();
246
 
                break;
247
 
            case XK_Tab:
248
 
                tabCompleteHistory();
249
 
                setCursorPosition(cp);
250
 
                break;
251
 
            }
252
 
        } else if ((ke.state & (Mod1Mask|ShiftMask)) == (Mod1Mask | ShiftMask)) {
253
 
            switch (ks) {
254
 
            case XK_less:
255
 
                firstHistoryItem();
256
 
                break;
257
 
            case XK_greater:
258
 
                lastHistoryItem();
259
 
                break;
260
 
            }
261
 
        }
262
 
    } else { // no modifier key
263
 
        switch (ks) {
264
 
        case XK_Escape:
265
 
            m_end = true;
266
 
            hide();
267
 
            FbTk::App::instance()->end(); // end program
268
 
            break;
269
 
        case XK_KP_Enter:
270
 
        case XK_Return:
271
 
            run(text());
272
 
            break;
273
 
        case XK_Up:
274
 
            prevHistoryItem();
275
 
            break;
276
 
        case XK_Down:
277
 
            nextHistoryItem();
278
 
            break;
279
 
        case XK_Tab:
280
 
            tabCompleteApps();
281
 
            setCursorPosition(cp);
282
 
            break;
283
 
        }
284
 
    }
285
 
    clear();
286
 
}
287
 
 
288
 
void FbRun::setNoMaximize() {
289
 
    // we don't need to maximize this window
290
 
    XSizeHints sh;
291
 
    sh.flags = PMaxSize | PMinSize;
292
 
    sh.max_width = width();
293
 
    sh.max_height = height();
294
 
    sh.min_width = width();
295
 
    sh.min_height = height();
296
 
    XSetWMNormalHints(m_display, window(), &sh);
297
 
}
298
 
 
299
 
void FbRun::prevHistoryItem() {
300
 
    if (m_history.size() == 0 || m_current_history_item == 0) {
301
 
        XBell(m_display, 0);
302
 
    } else {
303
 
        m_current_history_item--;
304
 
        setText(m_history[m_current_history_item]);
305
 
    }
306
 
}
307
 
 
308
 
void FbRun::nextHistoryItem() {
309
 
    if (m_current_history_item == m_history.size()) {
310
 
        XBell(m_display, 0);
311
 
    } else {
312
 
        m_current_history_item++;
313
 
        if (m_current_history_item == m_history.size()) {
314
 
            m_current_history_item = m_history.size();
315
 
            setText("");
316
 
         } else
317
 
            setText(m_history[m_current_history_item]);
318
 
    }
319
 
}
320
 
 
321
 
void FbRun::firstHistoryItem() {
322
 
    if (m_history.size() == 0 || m_current_history_item == 0) {
323
 
        XBell(m_display, 0);
324
 
    } else {
325
 
        m_current_history_item = 0;
326
 
        setText(m_history[m_current_history_item]);
327
 
    }
328
 
}
329
 
 
330
 
void FbRun::lastHistoryItem() {
331
 
    // actually one past the end
332
 
    if (m_history.size() == 0) {
333
 
        XBell(m_display, 0);
334
 
    } else {
335
 
        m_current_history_item = m_history.size();
336
 
        setText("");
337
 
    }
338
 
}
339
 
 
340
 
void FbRun::tabCompleteHistory() {
341
 
    if (m_current_history_item == 0 || m_history.empty() ) {
342
 
        XBell(m_display, 0);
343
 
    } else {
344
 
        unsigned int nr= 0;
345
 
        int history_item = m_current_history_item - 1;
346
 
        string prefix = text().substr(0, textStartPos() + cursorPosition());
347
 
        while (history_item != m_current_history_item && nr++ < m_history.size()) {
348
 
            if (history_item <= -1 )
349
 
                history_item= m_history.size() - 1;
350
 
            if (m_history[history_item].find(prefix) == 0) {
351
 
                m_current_history_item = history_item;
352
 
                setText(m_history[m_current_history_item]);
353
 
                break;
354
 
            }
355
 
            history_item--;
356
 
        }
357
 
        if (history_item == m_current_history_item) XBell(m_display, 0);
358
 
    }
359
 
}
360
 
 
361
 
void FbRun::tabCompleteApps() {
362
 
  
363
 
    static bool first_run= true;
364
 
    static string saved_prefix= "";
365
 
    string prefix= text().substr(0, textStartPos() + cursorPosition());
366
 
    FbTk::Directory dir;
367
 
 
368
 
    bool add_dirs= false;
369
 
    bool changed_prefix= false;
370
 
 
371
 
    // (re)build m_apps-container
372
 
    if (first_run || saved_prefix != prefix) {
373
 
        first_run= false;
374
 
        
375
 
        string path;
376
 
        
377
 
        if(!prefix.empty() && 
378
 
            string("/.~").find_first_of(prefix[0]) != string::npos) {
379
 
            size_t rseparator= prefix.find_last_of("/");
380
 
            path= prefix.substr(0, rseparator + 1) +  ":";
381
 
            add_dirs= true;
382
 
        } else
383
 
            path= getenv("PATH");
384
 
 
385
 
        m_apps.clear();
386
 
        
387
 
        unsigned int l;
388
 
        unsigned int r;
389
 
 
390
 
        for(l= 0, r= 0; r < path.size(); r++) {
391
 
            if ((path[r]==':' || r == path.size() - 1) && r - l > 0) {
392
 
                string filename;
393
 
                string fncomplete;
394
 
                dir.open(path.substr(l, r - l).c_str());
395
 
                int n= dir.entries();
396
 
                if (n >= 0) {
397
 
                    while(n--) {
398
 
                        filename= dir.readFilename();
399
 
                        fncomplete= dir.name() + 
400
 
                                    (*dir.name().rbegin() != '/' ? "/" : "") + 
401
 
                                    filename;
402
 
 
403
 
                        // directories in dirmode ?
404
 
                        if (add_dirs && FbTk::FileUtil::isDirectory(fncomplete.c_str()) &&
405
 
                            filename != ".." && filename != ".") {
406
 
                            m_apps.push_back(fncomplete); 
407
 
                        // executables in dirmode ?
408
 
                        } else if (add_dirs && FbTk::FileUtil::isRegularFile(fncomplete.c_str()) && 
409
 
                                   FbTk::FileUtil::isExecutable(fncomplete.c_str()) && 
410
 
                                   (prefix == "" || 
411
 
                                    fncomplete.substr(0, prefix.size()) == prefix)) {
412
 
                            m_apps.push_back(fncomplete);
413
 
                        // executables in $PATH ?
414
 
                        } else if (FbTk::FileUtil::isRegularFile(fncomplete.c_str()) && 
415
 
                                   FbTk::FileUtil::isExecutable(fncomplete.c_str()) && 
416
 
                                   (prefix == "" || 
417
 
                                    filename.substr(0, prefix.size()) == prefix)) {
418
 
                            m_apps.push_back(filename);
419
 
                        } 
420
 
                    }
421
 
                }
422
 
                l= r + 1;
423
 
                dir.close();
424
 
            }
425
 
        }
426
 
        sort(m_apps.begin(), m_apps.end());
427
 
        unique(m_apps.begin(), m_apps.end());
428
 
 
429
 
        saved_prefix= prefix;
430
 
        changed_prefix= true;
431
 
        m_current_apps_item= 0;
432
 
    }
433
 
 
434
 
    if (m_apps.empty() ) {
435
 
      XBell(m_display, 0);
436
 
    } else {
437
 
        size_t apps_item = m_current_apps_item + (changed_prefix ? 0 : 1);
438
 
        bool loop= false;
439
 
 
440
 
        while (true) {
441
 
            if (apps_item >= m_apps.size() ) {
442
 
                loop = true;
443
 
                apps_item = 0;
444
 
            }
445
 
 
446
 
            if ((!changed_prefix || loop) && apps_item == m_current_apps_item) {
447
 
                break;
448
 
            }
449
 
            if (m_apps[apps_item].find(prefix) == 0) {
450
 
                m_current_apps_item = apps_item;
451
 
                if (add_dirs && FbTk::FileUtil::isDirectory(m_apps[m_current_apps_item].c_str()))
452
 
                    setText(m_apps[m_current_apps_item] +  "/");
453
 
                else
454
 
                    setText(m_apps[m_current_apps_item]);
455
 
                break;
456
 
            }
457
 
            apps_item++;
458
 
        }
459
 
        if (!changed_prefix && apps_item == m_current_apps_item) 
460
 
            XBell(m_display, 0);
461
 
    }
462
 
}
463
 
 
464
 
void FbRun::insertCharacter(char keychar) {
465
 
    char val[2] = {keychar, 0};
466
 
    insertText(val);
467
 
}
468