Panel.c 11.6 KB
Newer Older
Hisham Muhammad's avatar
Hisham Muhammad committed
1
/*
2
htop - Panel.c
Hisham Muhammad's avatar
Hisham Muhammad committed
3
(C) 2004-2011 Hisham H. Muhammad
Hisham Muhammad's avatar
Hisham Muhammad committed
4
5
6
7
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/

8
#include "Panel.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
9

Hisham Muhammad's avatar
Hisham Muhammad committed
10
11
#include "CRT.h"
#include "RichString.h"
12
#include "ListItem.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
13
#include "String.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
14
15
16

#include <math.h>
#include <stdbool.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
17
18
19
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
20
21
#include <assert.h>
#include <curses.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
22

Hisham Muhammad's avatar
Hisham Muhammad committed
23
24
25
//#link curses

/*{
Hisham Muhammad's avatar
Hisham Muhammad committed
26
27
#include "Object.h"
#include "Vector.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
28

29
typedef struct Panel_ Panel;
Hisham Muhammad's avatar
Hisham Muhammad committed
30
31
32
33
34
35
36

typedef enum HandlerResult_ {
   HANDLED,
   IGNORED,
   BREAK_LOOP
} HandlerResult;

37
38
#define EVENT_SETSELECTED -1

39
typedef HandlerResult(*Panel_EventHandler)(Panel*, int);
Hisham Muhammad's avatar
Hisham Muhammad committed
40

41
42
43
44
45
46
47
48
49
typedef struct PanelClass_ {
   const ObjectClass super;
   const Panel_EventHandler eventHandler;
} PanelClass;

#define As_Panel(this_)                ((PanelClass*)((this_)->super.klass))
#define Panel_eventHandlerFn(this_)    As_Panel(this_)->eventHandler
#define Panel_eventHandler(this_, ev_) As_Panel(this_)->eventHandler((Panel*)(this_), ev_)

50
struct Panel_ {
Hisham Muhammad's avatar
Hisham Muhammad committed
51
   Object super;
52
   PanelClass* class;
Hisham Muhammad's avatar
Hisham Muhammad committed
53
54
   int x, y, w, h;
   WINDOW* window;
55
   Vector* items;
Hisham Muhammad's avatar
Hisham Muhammad committed
56
57
   int selected;
   int scrollV, scrollH;
58
   int scrollHAmount;
Hisham Muhammad's avatar
Hisham Muhammad committed
59
60
61
   int oldSelected;
   bool needsRedraw;
   RichString header;
62
   char* eventHandlerBuffer;
Hisham Muhammad's avatar
Hisham Muhammad committed
63
64
65
66
67
68
69
70
71
72
73
};

}*/

#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif

74
75
76
77
#define KEY_CTRLN      0016            /* control-n key */
#define KEY_CTRLP      0020            /* control-p key */
#define KEY_CTRLF      0006            /* control-f key */
#define KEY_CTRLB      0002            /* control-b key */
Hisham Muhammad's avatar
Hisham Muhammad committed
78

79
80
81
82
83
84
85
86
87
PanelClass Panel_class = {
   .super = {
      .extends = Class(Object),
      .delete = Panel_delete
   },
   .eventHandler = Panel_selectByTyping
};

Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type) {
88
89
   Panel* this;
   this = malloc(sizeof(Panel));
90
   Object_setClass(this, Class(Panel));
91
   Panel_init(this, x, y, w, h, type, owner);
Hisham Muhammad's avatar
Hisham Muhammad committed
92
93
94
   return this;
}

95
96
97
void Panel_delete(Object* cast) {
   Panel* this = (Panel*)cast;
   Panel_done(this);
Hisham Muhammad's avatar
Hisham Muhammad committed
98
99
100
   free(this);
}

101
void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner) {
Hisham Muhammad's avatar
Hisham Muhammad committed
102
103
104
105
   this->x = x;
   this->y = y;
   this->w = w;
   this->h = h;
106
   this->eventHandlerBuffer = NULL;
107
   this->items = Vector_new(type, owner, DEFAULT_SIZE);
Hisham Muhammad's avatar
Hisham Muhammad committed
108
109
110
111
112
   this->scrollV = 0;
   this->scrollH = 0;
   this->selected = 0;
   this->oldSelected = 0;
   this->needsRedraw = true;
113
   RichString_beginAllocated(this->header);
114
   if (String_eq(CRT_termType, "linux"))
Hisham Muhammad's avatar
Hisham Muhammad committed
115
      this->scrollHAmount = 20;
116
117
   else
      this->scrollHAmount = 5;
Hisham Muhammad's avatar
Hisham Muhammad committed
118
119
}

120
void Panel_done(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
121
   assert (this != NULL);
122
   free(this->eventHandlerBuffer);
123
   Vector_delete(this->items);
124
   RichString_end(this->header);
Hisham Muhammad's avatar
Hisham Muhammad committed
125
126
}

127
RichString* Panel_getHeader(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
128
129
130
   assert (this != NULL);

   this->needsRedraw = true;
131
   return &(this->header);
Hisham Muhammad's avatar
Hisham Muhammad committed
132
133
}

Hisham Muhammad's avatar
Hisham Muhammad committed
134
inline void Panel_setHeader(Panel* this, const char* header) {
135
136
   RichString_write(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
   this->needsRedraw = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
137
138
}

139
void Panel_move(Panel* this, int x, int y) {
Hisham Muhammad's avatar
Hisham Muhammad committed
140
141
142
143
144
145
146
   assert (this != NULL);

   this->x = x;
   this->y = y;
   this->needsRedraw = true;
}

147
void Panel_resize(Panel* this, int w, int h) {
Hisham Muhammad's avatar
Hisham Muhammad committed
148
149
   assert (this != NULL);

150
   if (RichString_sizeVal(this->header) > 0)
Hisham Muhammad's avatar
Hisham Muhammad committed
151
152
153
154
155
156
      h--;
   this->w = w;
   this->h = h;
   this->needsRedraw = true;
}

157
void Panel_prune(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
158
159
   assert (this != NULL);

160
   Vector_prune(this->items);
Hisham Muhammad's avatar
Hisham Muhammad committed
161
162
163
164
165
166
   this->scrollV = 0;
   this->selected = 0;
   this->oldSelected = 0;
   this->needsRedraw = true;
}

167
void Panel_add(Panel* this, Object* o) {
Hisham Muhammad's avatar
Hisham Muhammad committed
168
169
   assert (this != NULL);

170
   Vector_add(this->items, o);
Hisham Muhammad's avatar
Hisham Muhammad committed
171
172
173
   this->needsRedraw = true;
}

174
void Panel_insert(Panel* this, int i, Object* o) {
Hisham Muhammad's avatar
Hisham Muhammad committed
175
176
   assert (this != NULL);

177
   Vector_insert(this->items, i, o);
Hisham Muhammad's avatar
Hisham Muhammad committed
178
179
180
   this->needsRedraw = true;
}

181
void Panel_set(Panel* this, int i, Object* o) {
Hisham Muhammad's avatar
Hisham Muhammad committed
182
183
   assert (this != NULL);

184
   Vector_set(this->items, i, o);
Hisham Muhammad's avatar
Hisham Muhammad committed
185
186
}

187
Object* Panel_get(Panel* this, int i) {
Hisham Muhammad's avatar
Hisham Muhammad committed
188
189
   assert (this != NULL);

190
   return Vector_get(this->items, i);
Hisham Muhammad's avatar
Hisham Muhammad committed
191
192
}

193
Object* Panel_remove(Panel* this, int i) {
Hisham Muhammad's avatar
Hisham Muhammad committed
194
195
196
   assert (this != NULL);

   this->needsRedraw = true;
197
198
   Object* removed = Vector_remove(this->items, i);
   if (this->selected > 0 && this->selected >= Vector_size(this->items))
Hisham Muhammad's avatar
Hisham Muhammad committed
199
200
201
202
      this->selected--;
   return removed;
}

203
Object* Panel_getSelected(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
204
   assert (this != NULL);
205
206
207
208
   if (Vector_size(this->items) > 0)
      return Vector_get(this->items, this->selected);
   else
      return NULL;
Hisham Muhammad's avatar
Hisham Muhammad committed
209
210
}

211
void Panel_moveSelectedUp(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
212
213
   assert (this != NULL);

214
   Vector_moveUp(this->items, this->selected);
Hisham Muhammad's avatar
Hisham Muhammad committed
215
216
217
218
   if (this->selected > 0)
      this->selected--;
}

219
void Panel_moveSelectedDown(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
220
221
   assert (this != NULL);

222
223
   Vector_moveDown(this->items, this->selected);
   if (this->selected + 1 < Vector_size(this->items))
Hisham Muhammad's avatar
Hisham Muhammad committed
224
225
226
      this->selected++;
}

227
int Panel_getSelectedIndex(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
228
229
230
231
232
   assert (this != NULL);

   return this->selected;
}

Hisham Muhammad's avatar
Hisham Muhammad committed
233
int Panel_size(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
234
235
   assert (this != NULL);

236
   return Vector_size(this->items);
Hisham Muhammad's avatar
Hisham Muhammad committed
237
238
}

239
void Panel_setSelected(Panel* this, int selected) {
Hisham Muhammad's avatar
Hisham Muhammad committed
240
241
   assert (this != NULL);

242
   selected = MAX(0, MIN(Vector_size(this->items) - 1, selected));
Hisham Muhammad's avatar
Hisham Muhammad committed
243
   this->selected = selected;
244
245
   if (Panel_eventHandlerFn(this)) {
      Panel_eventHandler(this, EVENT_SETSELECTED);
246
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
247
248
}

249
void Panel_draw(Panel* this, bool focus) {
Hisham Muhammad's avatar
Hisham Muhammad committed
250
251
   assert (this != NULL);

252
   int itemCount = Vector_size(this->items);
Hisham Muhammad's avatar
Hisham Muhammad committed
253
254
   int scrollH = this->scrollH;
   int y = this->y; int x = this->x;
255
   int first = this->scrollV;
256
257
258
259
260
   if (itemCount > this->h && first > itemCount - this->h) {
      first = itemCount - this->h;
      this->scrollV = first;
   }
   int last = MIN(itemCount, first + MIN(itemCount, this->h));
Hisham Muhammad's avatar
Hisham Muhammad committed
261
262
263
264
265
266
267
268
269
270
271
272
273
274
   if (this->selected < first) {
      first = this->selected;
      this->scrollV = first;
      this->needsRedraw = true;
   }
   if (this->selected >= last) {
      last = MIN(itemCount, this->selected + 1);
      first = MAX(0, last - this->h);
      this->scrollV = first;
      this->needsRedraw = true;
   }
   assert(first >= 0);
   assert(last <= itemCount);

275
276
   int headerLen = RichString_sizeVal(this->header);
   if (headerLen > 0) {
Hisham Muhammad's avatar
Hisham Muhammad committed
277
278
279
280
281
      int attr = focus
               ? CRT_colors[PANEL_HEADER_FOCUS]
               : CRT_colors[PANEL_HEADER_UNFOCUS];
      attrset(attr);
      mvhline(y, x, ' ', this->w);
282
      if (scrollH < headerLen) {
283
         RichString_printoffnVal(this->header, y, x, scrollH,
284
            MIN(headerLen - scrollH, this->w));
Hisham Muhammad's avatar
Hisham Muhammad committed
285
286
287
288
289
290
291
292
293
294
295
296
      }
      attrset(CRT_colors[RESET_COLOR]);
      y++;
   }
   
   int highlight = focus
                 ? CRT_colors[PANEL_HIGHLIGHT_FOCUS]
                 : CRT_colors[PANEL_HIGHLIGHT_UNFOCUS];

   if (this->needsRedraw) {

      for(int i = first, j = 0; j < this->h && i < last; i++, j++) {
297
         Object* itemObj = Vector_get(this->items, i);
298
         RichString_begin(item);
299
         Object_display(itemObj, &item);
300
301
         int itemLen = RichString_sizeVal(item);
         int amt = MIN(itemLen - scrollH, this->w);
Hisham Muhammad's avatar
Hisham Muhammad committed
302
303
         if (i == this->selected) {
            attrset(highlight);
304
            RichString_setAttr(&item, highlight);
Hisham Muhammad's avatar
Hisham Muhammad committed
305
306
            mvhline(y + j, x+0, ' ', this->w);
            if (amt > 0)
307
               RichString_printoffnVal(item, y+j, x+0, scrollH, amt);
Hisham Muhammad's avatar
Hisham Muhammad committed
308
309
310
311
            attrset(CRT_colors[RESET_COLOR]);
         } else {
            mvhline(y+j, x+0, ' ', this->w);
            if (amt > 0)
312
               RichString_printoffnVal(item, y+j, x+0, scrollH, amt);
Hisham Muhammad's avatar
Hisham Muhammad committed
313
         }
314
         RichString_end(item);
Hisham Muhammad's avatar
Hisham Muhammad committed
315
316
317
318
319
320
      }
      for (int i = y + (last - first); i < y + this->h; i++)
         mvhline(i, x+0, ' ', this->w);
      this->needsRedraw = false;

   } else {
321
      Object* oldObj = Vector_get(this->items, this->oldSelected);
322
      RichString_begin(old);
323
      Object_display(oldObj, &old);
324
      int oldLen = RichString_sizeVal(old);
325
      Object* newObj = Vector_get(this->items, this->selected);
326
      RichString_begin(new);
327
      Object_display(newObj, &new);
328
      int newLen = RichString_sizeVal(new);
Hisham Muhammad's avatar
Hisham Muhammad committed
329
      mvhline(y+ this->oldSelected - this->scrollV, x+0, ' ', this->w);
330
331
332
      if (scrollH < oldLen)
         RichString_printoffnVal(old, y+this->oldSelected - this->scrollV, x,
            this->scrollH, MIN(oldLen - scrollH, this->w));
Hisham Muhammad's avatar
Hisham Muhammad committed
333
334
      attrset(highlight);
      mvhline(y+this->selected - this->scrollV, x+0, ' ', this->w);
335
336
337
338
      RichString_setAttr(&new, highlight);
      if (scrollH < newLen)
         RichString_printoffnVal(new, y+this->selected - this->scrollV, x,
            this->scrollH, MIN(newLen - scrollH, this->w));
Hisham Muhammad's avatar
Hisham Muhammad committed
339
      attrset(CRT_colors[RESET_COLOR]);
340
341
      RichString_end(new);
      RichString_end(old);
Hisham Muhammad's avatar
Hisham Muhammad committed
342
343
344
345
346
   }
   this->oldSelected = this->selected;
   move(0, 0);
}

Hisham Muhammad's avatar
Hisham Muhammad committed
347
bool Panel_onKey(Panel* this, int key) {
Hisham Muhammad's avatar
Hisham Muhammad committed
348
349
350
   assert (this != NULL);
   switch (key) {
   case KEY_DOWN:
351
   case KEY_CTRLN:
352
      if (this->selected + 1 < Vector_size(this->items))
Hisham Muhammad's avatar
Hisham Muhammad committed
353
         this->selected++;
Hisham Muhammad's avatar
Hisham Muhammad committed
354
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
355
   case KEY_UP:
356
   case KEY_CTRLP:
Hisham Muhammad's avatar
Hisham Muhammad committed
357
358
      if (this->selected > 0)
         this->selected--;
Hisham Muhammad's avatar
Hisham Muhammad committed
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
      return true;
   #ifdef KEY_C_DOWN
   case KEY_C_DOWN:
      if (this->selected + 1 < Vector_size(this->items)) {
         this->selected++;
         if (this->scrollV < Vector_size(this->items) - this->h) {
            this->scrollV++;
            this->needsRedraw = true;
         }
      }
      return true;
   #endif
   #ifdef KEY_C_UP
   case KEY_C_UP:
      if (this->selected > 0) {
         this->selected--;
         if (this->scrollV > 0) {
            this->scrollV--;
            this->needsRedraw = true;
         }
      }
      return true;
   #endif
Hisham Muhammad's avatar
Hisham Muhammad committed
382
   case KEY_LEFT:
383
   case KEY_CTRLB:
Hisham Muhammad's avatar
Hisham Muhammad committed
384
      if (this->scrollH > 0) {
Hisham Muhammad's avatar
Hisham Muhammad committed
385
         this->scrollH -= 5;
Hisham Muhammad's avatar
Hisham Muhammad committed
386
387
         this->needsRedraw = true;
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
388
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
389
   case KEY_RIGHT:
390
   case KEY_CTRLF:
Hisham Muhammad's avatar
Hisham Muhammad committed
391
      this->scrollH += 5;
Hisham Muhammad's avatar
Hisham Muhammad committed
392
      this->needsRedraw = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
393
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
394
   case KEY_PPAGE:
Hisham Muhammad's avatar
Hisham Muhammad committed
395
396
      this->selected -= (this->h - 1);
      this->scrollV -= (this->h - 1);
Hisham Muhammad's avatar
Hisham Muhammad committed
397
398
      if (this->selected < 0)
         this->selected = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
399
400
401
402
      if (this->scrollV < 0)
         this->scrollV = 0;
      this->needsRedraw = true;
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
403
   case KEY_NPAGE:
Hisham Muhammad's avatar
Hisham Muhammad committed
404
      this->selected += (this->h - 1);
405
      int size = Vector_size(this->items);
Hisham Muhammad's avatar
Hisham Muhammad committed
406
407
      if (this->selected >= size)
         this->selected = size - 1;
Hisham Muhammad's avatar
Hisham Muhammad committed
408
409
410
411
412
      this->scrollV += (this->h - 1);
      if (this->scrollV >= MAX(0, size - this->h))
         this->scrollV = MAX(0, size - this->h - 1);
      this->needsRedraw = true;
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
413
414
   case KEY_HOME:
      this->selected = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
415
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
416
   case KEY_END:
417
      this->selected = Vector_size(this->items) - 1;
Hisham Muhammad's avatar
Hisham Muhammad committed
418
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
419
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
420
   return false;
Hisham Muhammad's avatar
Hisham Muhammad committed
421
}
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456


HandlerResult Panel_selectByTyping(Panel* this, int ch) {
   int size = Panel_size(this);
   if (!this->eventHandlerBuffer)
      this->eventHandlerBuffer = calloc(100, 1);

   if (isalnum(ch)) {
      int len = strlen(this->eventHandlerBuffer);
      if (len < 99) {
         this->eventHandlerBuffer[len] = ch;
         this->eventHandlerBuffer[len+1] = '\0';
      }
      for (int try = 0; try < 2; try++) {
         len = strlen(this->eventHandlerBuffer);
         for (int i = 0; i < size; i++) {
            char* cur = ((ListItem*) Panel_get(this, i))->value;
            while (*cur == ' ') cur++;
            if (strncasecmp(cur, this->eventHandlerBuffer, len) == 0) {
               Panel_setSelected(this, i);
               return HANDLED;
            }
         }
         this->eventHandlerBuffer[0] = ch;
         this->eventHandlerBuffer[1] = '\0';
      }
      return HANDLED;
   } else if (ch != ERR) {
      this->eventHandlerBuffer[0] = '\0';
   }
   if (ch == 13) {
      return BREAK_LOOP;
   }
   return IGNORED;
}