Panel.c 11.4 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
#include <assert.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
21

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

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

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

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

36
37
#define EVENT_SETSELECTED -1

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

40
41
42
43
44
45
46
47
48
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_)

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

}*/

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

73
74
75
76
#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
77

78
79
80
81
82
83
84
85
86
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) {
87
88
   Panel* this;
   this = malloc(sizeof(Panel));
89
   Object_setClass(this, Class(Panel));
90
   Panel_init(this, x, y, w, h, type, owner);
Hisham Muhammad's avatar
Hisham Muhammad committed
91
92
93
   return this;
}

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

100
void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner) {
Hisham Muhammad's avatar
Hisham Muhammad committed
101
102
103
104
   this->x = x;
   this->y = y;
   this->w = w;
   this->h = h;
105
   this->eventHandlerBuffer = NULL;
106
   this->items = Vector_new(type, owner, DEFAULT_SIZE);
Hisham Muhammad's avatar
Hisham Muhammad committed
107
108
109
110
111
   this->scrollV = 0;
   this->scrollH = 0;
   this->selected = 0;
   this->oldSelected = 0;
   this->needsRedraw = true;
112
   RichString_beginAllocated(this->header);
Hisham Muhammad's avatar
Hisham Muhammad committed
113
114
}

115
void Panel_done(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
116
   assert (this != NULL);
117
   free(this->eventHandlerBuffer);
118
   Vector_delete(this->items);
119
   RichString_end(this->header);
Hisham Muhammad's avatar
Hisham Muhammad committed
120
121
}

122
RichString* Panel_getHeader(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
123
124
125
   assert (this != NULL);

   this->needsRedraw = true;
126
   return &(this->header);
Hisham Muhammad's avatar
Hisham Muhammad committed
127
128
}

Hisham Muhammad's avatar
Hisham Muhammad committed
129
inline void Panel_setHeader(Panel* this, const char* header) {
130
131
   RichString_write(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
   this->needsRedraw = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
132
133
}

134
void Panel_move(Panel* this, int x, int y) {
Hisham Muhammad's avatar
Hisham Muhammad committed
135
136
137
138
139
140
141
   assert (this != NULL);

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

142
void Panel_resize(Panel* this, int w, int h) {
Hisham Muhammad's avatar
Hisham Muhammad committed
143
144
   assert (this != NULL);

145
   if (RichString_sizeVal(this->header) > 0)
Hisham Muhammad's avatar
Hisham Muhammad committed
146
147
148
149
150
151
      h--;
   this->w = w;
   this->h = h;
   this->needsRedraw = true;
}

152
void Panel_prune(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
153
154
   assert (this != NULL);

155
   Vector_prune(this->items);
Hisham Muhammad's avatar
Hisham Muhammad committed
156
157
158
159
160
161
   this->scrollV = 0;
   this->selected = 0;
   this->oldSelected = 0;
   this->needsRedraw = true;
}

162
void Panel_add(Panel* this, Object* o) {
Hisham Muhammad's avatar
Hisham Muhammad committed
163
164
   assert (this != NULL);

165
   Vector_add(this->items, o);
Hisham Muhammad's avatar
Hisham Muhammad committed
166
167
168
   this->needsRedraw = true;
}

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

172
   Vector_insert(this->items, i, o);
Hisham Muhammad's avatar
Hisham Muhammad committed
173
174
175
   this->needsRedraw = true;
}

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

179
   Vector_set(this->items, i, o);
Hisham Muhammad's avatar
Hisham Muhammad committed
180
181
}

182
Object* Panel_get(Panel* this, int i) {
Hisham Muhammad's avatar
Hisham Muhammad committed
183
184
   assert (this != NULL);

185
   return Vector_get(this->items, i);
Hisham Muhammad's avatar
Hisham Muhammad committed
186
187
}

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

   this->needsRedraw = true;
192
193
   Object* removed = Vector_remove(this->items, i);
   if (this->selected > 0 && this->selected >= Vector_size(this->items))
Hisham Muhammad's avatar
Hisham Muhammad committed
194
195
196
197
      this->selected--;
   return removed;
}

198
Object* Panel_getSelected(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
199
   assert (this != NULL);
200
201
202
203
   if (Vector_size(this->items) > 0)
      return Vector_get(this->items, this->selected);
   else
      return NULL;
Hisham Muhammad's avatar
Hisham Muhammad committed
204
205
}

206
void Panel_moveSelectedUp(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
207
208
   assert (this != NULL);

209
   Vector_moveUp(this->items, this->selected);
Hisham Muhammad's avatar
Hisham Muhammad committed
210
211
212
213
   if (this->selected > 0)
      this->selected--;
}

214
void Panel_moveSelectedDown(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
215
216
   assert (this != NULL);

217
218
   Vector_moveDown(this->items, this->selected);
   if (this->selected + 1 < Vector_size(this->items))
Hisham Muhammad's avatar
Hisham Muhammad committed
219
220
221
      this->selected++;
}

222
int Panel_getSelectedIndex(Panel* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
223
224
225
226
227
   assert (this != NULL);

   return this->selected;
}

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

231
   return Vector_size(this->items);
Hisham Muhammad's avatar
Hisham Muhammad committed
232
233
}

234
void Panel_setSelected(Panel* this, int selected) {
Hisham Muhammad's avatar
Hisham Muhammad committed
235
236
   assert (this != NULL);

237
   selected = MAX(0, MIN(Vector_size(this->items) - 1, selected));
Hisham Muhammad's avatar
Hisham Muhammad committed
238
   this->selected = selected;
239
240
   if (Panel_eventHandlerFn(this)) {
      Panel_eventHandler(this, EVENT_SETSELECTED);
241
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
242
243
}

244
void Panel_draw(Panel* this, bool focus) {
Hisham Muhammad's avatar
Hisham Muhammad committed
245
246
   assert (this != NULL);

247
   int itemCount = Vector_size(this->items);
Hisham Muhammad's avatar
Hisham Muhammad committed
248
249
   int scrollH = this->scrollH;
   int y = this->y; int x = this->x;
250
   int first = this->scrollV;
251
252
253
254
255
   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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
   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);

270
271
   int headerLen = RichString_sizeVal(this->header);
   if (headerLen > 0) {
Hisham Muhammad's avatar
Hisham Muhammad committed
272
273
274
275
276
      int attr = focus
               ? CRT_colors[PANEL_HEADER_FOCUS]
               : CRT_colors[PANEL_HEADER_UNFOCUS];
      attrset(attr);
      mvhline(y, x, ' ', this->w);
277
      if (scrollH < headerLen) {
278
         RichString_printoffnVal(this->header, y, x, scrollH,
279
            MIN(headerLen - scrollH, this->w));
Hisham Muhammad's avatar
Hisham Muhammad committed
280
281
282
283
284
285
286
287
288
289
290
291
      }
      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++) {
292
         Object* itemObj = Vector_get(this->items, i);
293
         assert(itemObj); if(!itemObj) continue;
294
         RichString_begin(item);
295
         Object_display(itemObj, &item);
296
297
         int itemLen = RichString_sizeVal(item);
         int amt = MIN(itemLen - scrollH, this->w);
Hisham Muhammad's avatar
Hisham Muhammad committed
298
299
         bool selected = (i == this->selected);
         if (selected) {
Hisham Muhammad's avatar
Hisham Muhammad committed
300
            attrset(highlight);
301
            RichString_setAttr(&item, highlight);
Hisham Muhammad's avatar
Hisham Muhammad committed
302
         }
Hisham Muhammad's avatar
Hisham Muhammad committed
303
304
305
306
307
         mvhline(y + j, x, ' ', this->w);
         if (amt > 0)
            RichString_printoffnVal(item, y+j, x, scrollH, amt);
         if (selected)
            attrset(CRT_colors[RESET_COLOR]);
308
         RichString_end(item);
Hisham Muhammad's avatar
Hisham Muhammad committed
309
310
311
312
313
314
      }
      for (int i = y + (last - first); i < y + this->h; i++)
         mvhline(i, x+0, ' ', this->w);
      this->needsRedraw = false;

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

Hisham Muhammad's avatar
Hisham Muhammad committed
342
bool Panel_onKey(Panel* this, int key) {
Hisham Muhammad's avatar
Hisham Muhammad committed
343
344
345
   assert (this != NULL);
   switch (key) {
   case KEY_DOWN:
346
   case KEY_CTRLN:
347
      if (this->selected + 1 < Vector_size(this->items))
Hisham Muhammad's avatar
Hisham Muhammad committed
348
         this->selected++;
Hisham Muhammad's avatar
Hisham Muhammad committed
349
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
350
   case KEY_UP:
351
   case KEY_CTRLP:
Hisham Muhammad's avatar
Hisham Muhammad committed
352
353
      if (this->selected > 0)
         this->selected--;
Hisham Muhammad's avatar
Hisham Muhammad committed
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
      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
377
   case KEY_LEFT:
378
   case KEY_CTRLB:
Hisham Muhammad's avatar
Hisham Muhammad committed
379
      if (this->scrollH > 0) {
Hisham Muhammad's avatar
Hisham Muhammad committed
380
         this->scrollH -= CRT_scrollHAmount;
Hisham Muhammad's avatar
Hisham Muhammad committed
381
382
         this->needsRedraw = true;
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
383
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
384
   case KEY_RIGHT:
385
   case KEY_CTRLF:
Hisham Muhammad's avatar
Hisham Muhammad committed
386
      this->scrollH += CRT_scrollHAmount;
Hisham Muhammad's avatar
Hisham Muhammad committed
387
      this->needsRedraw = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
388
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
389
   case KEY_PPAGE:
Hisham Muhammad's avatar
Hisham Muhammad committed
390
391
      this->selected -= (this->h - 1);
      this->scrollV -= (this->h - 1);
Hisham Muhammad's avatar
Hisham Muhammad committed
392
393
      if (this->selected < 0)
         this->selected = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
394
395
396
397
      if (this->scrollV < 0)
         this->scrollV = 0;
      this->needsRedraw = true;
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
398
   case KEY_NPAGE:
Hisham Muhammad's avatar
Hisham Muhammad committed
399
      this->selected += (this->h - 1);
400
      int size = Vector_size(this->items);
Hisham Muhammad's avatar
Hisham Muhammad committed
401
402
      if (this->selected >= size)
         this->selected = size - 1;
Hisham Muhammad's avatar
Hisham Muhammad committed
403
404
405
406
407
      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
408
409
   case KEY_HOME:
      this->selected = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
410
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
411
   case KEY_END:
412
      this->selected = Vector_size(this->items) - 1;
Hisham Muhammad's avatar
Hisham Muhammad committed
413
      return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
414
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
415
   return false;
Hisham Muhammad's avatar
Hisham Muhammad committed
416
}
417
418
419
420
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


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;
}