cmCursesMainForm.cxx 35.3 KB
Newer Older
1
2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
Brad King's avatar
Brad King committed
3
4
#include "cmCursesMainForm.h"

5
#include "cmAlgorithms.h"
Berk Geveci's avatar
Berk Geveci committed
6
#include "cmCursesCacheEntryComposite.h"
7
#include "cmCursesDummyWidget.h"
8
#include "cmCursesForm.h"
9
#include "cmCursesLabelWidget.h"
10
#include "cmCursesLongMessageForm.h"
11
#include "cmCursesStandardIncludes.h"
12
#include "cmCursesStringWidget.h"
13
#include "cmCursesWidget.h"
14
#include "cmState.h"
15
#include "cmStateTypes.h"
16
17
18
19
20
21
#include "cmSystemTools.h"
#include "cmVersion.h"
#include "cmake.h"

#include <stdio.h>
#include <string.h>
Berk Geveci's avatar
Berk Geveci committed
22

Berk Geveci's avatar
Berk Geveci committed
23
24
inline int ctrl(int z)
{
25
  return (z & 037);
26
}
Berk Geveci's avatar
Berk Geveci committed
27

28
cmCursesMainForm::cmCursesMainForm(std::vector<std::string> const& args,
29
30
31
                                   int initWidth)
  : Args(args)
  , InitialWidth(initWidth)
Berk Geveci's avatar
Berk Geveci committed
32
{
Ken Martin's avatar
Ken Martin committed
33
  this->NumberOfPages = 0;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
34
35
  this->Fields = CM_NULLPTR;
  this->Entries = CM_NULLPTR;
Ken Martin's avatar
Ken Martin committed
36
37
38
  this->AdvancedMode = false;
  this->NumberOfVisibleEntries = 0;
  this->OkToGenerate = false;
39
40
  this->HelpMessage.push_back(
    "Welcome to ccmake, curses based user interface for CMake.");
Ken Martin's avatar
Ken Martin committed
41
42
43
  this->HelpMessage.push_back("");
  this->HelpMessage.push_back(s_ConstHelpMessage);
  this->CMakeInstance = new cmake;
44
45
  this->CMakeInstance->SetCMakeEditCommand(
    cmSystemTools::GetCMakeCursesCommand());
Ken Martin's avatar
Ken Martin committed
46
47

  // create the arguments for the cmake object
48
  std::string whereCMake = cmSystemTools::GetProgramPath(this->Args[0]);
Ken Martin's avatar
Ken Martin committed
49
  whereCMake += "/cmake";
Ken Martin's avatar
Ken Martin committed
50
51
52
53
54
  this->Args[0] = whereCMake;
  this->CMakeInstance->SetArgs(this->Args);
  this->SearchString = "";
  this->OldSearchString = "";
  this->SearchMode = false;
Berk Geveci's avatar
Berk Geveci committed
55
56
57
58
}

cmCursesMainForm::~cmCursesMainForm()
{
59
  if (this->Form) {
Ken Martin's avatar
Ken Martin committed
60
61
    unpost_form(this->Form);
    free_form(this->Form);
Daniel Pfeifer's avatar
Daniel Pfeifer committed
62
    this->Form = CM_NULLPTR;
63
  }
Ken Martin's avatar
Ken Martin committed
64
  delete[] this->Fields;
Berk Geveci's avatar
Berk Geveci committed
65
66

  // Clean-up composites
67
  if (this->Entries) {
68
    cmDeleteAll(*this->Entries);
69
  }
Ken Martin's avatar
Ken Martin committed
70
  delete this->Entries;
71
  if (this->CMakeInstance) {
Ken Martin's avatar
Ken Martin committed
72
    delete this->CMakeInstance;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
73
    this->CMakeInstance = CM_NULLPTR;
74
  }
Berk Geveci's avatar
Berk Geveci committed
75
76
}

Berk Geveci's avatar
Berk Geveci committed
77
// See if a cache entry is in the list of entries in the ui.
78
bool cmCursesMainForm::LookForCacheEntry(const std::string& key)
Berk Geveci's avatar
Berk Geveci committed
79
{
80
  if (!this->Entries) {
Berk Geveci's avatar
Berk Geveci committed
81
    return false;
82
  }
Berk Geveci's avatar
Berk Geveci committed
83
84

  std::vector<cmCursesCacheEntryComposite*>::iterator it;
85
86
  for (it = this->Entries->begin(); it != this->Entries->end(); ++it) {
    if (key == (*it)->Key) {
Berk Geveci's avatar
Berk Geveci committed
87
88
      return true;
    }
89
  }
90

Berk Geveci's avatar
Berk Geveci committed
91
92
93
  return false;
}

Berk Geveci's avatar
Berk Geveci committed
94
// Create new cmCursesCacheEntryComposite entries from the cache
95
void cmCursesMainForm::InitializeUI()
Berk Geveci's avatar
Berk Geveci committed
96
{
Berk Geveci's avatar
Berk Geveci committed
97
98
  // Create a vector of cmCursesCacheEntryComposite's
  // which contain labels, entries and new entry markers
Berk Geveci's avatar
Berk Geveci committed
99
100
  std::vector<cmCursesCacheEntryComposite*>* newEntries =
    new std::vector<cmCursesCacheEntryComposite*>;
Stephen Kelly's avatar
Stephen Kelly committed
101
  std::vector<std::string> cacheKeys =
102
    this->CMakeInstance->GetState()->GetCacheEntryKeys();
Stephen Kelly's avatar
Stephen Kelly committed
103
  newEntries->reserve(cacheKeys.size());
Berk Geveci's avatar
Berk Geveci committed
104
105

  // Count non-internal and non-static entries
106
107
108
109
  int count = 0;

  for (std::vector<std::string>::const_iterator it = cacheKeys.begin();
       it != cacheKeys.end(); ++it) {
110
    cmStateEnums::CacheEntryType t =
111
      this->CMakeInstance->GetState()->GetCacheEntryType(*it);
112
113
    if (t != cmStateEnums::INTERNAL && t != cmStateEnums::STATIC &&
        t != cmStateEnums::UNINITIALIZED) {
Berk Geveci's avatar
Berk Geveci committed
114
115
      ++count;
    }
116
  }
Berk Geveci's avatar
Berk Geveci committed
117

Ken Martin's avatar
Ken Martin committed
118
  int entrywidth = this->InitialWidth - 35;
119

Berk Geveci's avatar
Berk Geveci committed
120
  cmCursesCacheEntryComposite* comp;
121
  if (count == 0) {
Berk Geveci's avatar
Berk Geveci committed
122
123
    // If cache is empty, display a label saying so and a
    // dummy entry widget (does not respond to input)
124
    comp = new cmCursesCacheEntryComposite("EMPTY CACHE", 30, 30);
Ken Martin's avatar
Ken Martin committed
125
    comp->Entry = new cmCursesDummyWidget(1, 1, 1, 1);
Berk Geveci's avatar
Berk Geveci committed
126
    newEntries->push_back(comp);
127
  } else {
Berk Geveci's avatar
Berk Geveci committed
128
129
130
    // Create the composites.

    // First add entries which are new
131
132
    for (std::vector<std::string>::const_iterator it = cacheKeys.begin();
         it != cacheKeys.end(); ++it) {
133
      std::string key = *it;
134
      cmStateEnums::CacheEntryType t =
135
        this->CMakeInstance->GetState()->GetCacheEntryType(*it);
136
137
      if (t == cmStateEnums::INTERNAL || t == cmStateEnums::STATIC ||
          t == cmStateEnums::UNINITIALIZED) {
138
        continue;
139
      }
Berk Geveci's avatar
Berk Geveci committed
140

141
142
143
      if (!this->LookForCacheEntry(key)) {
        newEntries->push_back(new cmCursesCacheEntryComposite(
          key, this->CMakeInstance, true, 30, entrywidth));
Ken Martin's avatar
Ken Martin committed
144
        this->OkToGenerate = false;
Berk Geveci's avatar
Berk Geveci committed
145
      }
146
    }
Berk Geveci's avatar
Berk Geveci committed
147
148

    // then add entries which are old
149
150
    for (std::vector<std::string>::const_iterator it = cacheKeys.begin();
         it != cacheKeys.end(); ++it) {
151
      std::string key = *it;
152
      cmStateEnums::CacheEntryType t =
153
        this->CMakeInstance->GetState()->GetCacheEntryType(*it);
154
155
      if (t == cmStateEnums::INTERNAL || t == cmStateEnums::STATIC ||
          t == cmStateEnums::UNINITIALIZED) {
156
        continue;
157
      }
Berk Geveci's avatar
Berk Geveci committed
158

159
160
161
      if (this->LookForCacheEntry(key)) {
        newEntries->push_back(new cmCursesCacheEntryComposite(
          key, this->CMakeInstance, false, 30, entrywidth));
Berk Geveci's avatar
Berk Geveci committed
162
163
      }
    }
164
  }
165

Berk Geveci's avatar
Berk Geveci committed
166
  // Clean old entries
167
  if (this->Entries) {
168
    cmDeleteAll(*this->Entries);
169
  }
Ken Martin's avatar
Ken Martin committed
170
171
  delete this->Entries;
  this->Entries = newEntries;
172

Berk Geveci's avatar
Berk Geveci committed
173
  // Compute fields from composites
174
175
176
177
178
  this->RePost();
}

void cmCursesMainForm::RePost()
{
Berk Geveci's avatar
Berk Geveci committed
179
  // Create the fields to be passed to the form.
180
  if (this->Form) {
Ken Martin's avatar
Ken Martin committed
181
182
    unpost_form(this->Form);
    free_form(this->Form);
Daniel Pfeifer's avatar
Daniel Pfeifer committed
183
    this->Form = CM_NULLPTR;
184
  }
Ken Martin's avatar
Ken Martin committed
185
  delete[] this->Fields;
186
  if (this->AdvancedMode) {
Ken Martin's avatar
Ken Martin committed
187
    this->NumberOfVisibleEntries = this->Entries->size();
188
  } else {
Berk Geveci's avatar
Berk Geveci committed
189
    // If normal mode, count only non-advanced entries
Ken Martin's avatar
Ken Martin committed
190
    this->NumberOfVisibleEntries = 0;
191
    std::vector<cmCursesCacheEntryComposite*>::iterator it;
192
    for (it = this->Entries->begin(); it != this->Entries->end(); ++it) {
193
      const char* existingValue =
194
        this->CMakeInstance->GetState()->GetCacheEntryValue((*it)->GetValue());
195
      bool advanced =
196
197
198
        this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool(
          (*it)->GetValue(), "ADVANCED");
      if (!existingValue || (!this->AdvancedMode && advanced)) {
199
        continue;
200
      }
201
      this->NumberOfVisibleEntries++;
202
    }
203
  }
204
  // there is always one even if it is the dummy one
205
  if (this->NumberOfVisibleEntries == 0) {
206
    this->NumberOfVisibleEntries = 1;
207
  }
Berk Geveci's avatar
Berk Geveci committed
208
209
  // Assign the fields: 3 for each entry: label, new entry marker
  // ('*' or ' ') and entry widget
210
  this->Fields = new FIELD*[3 * this->NumberOfVisibleEntries + 1];
211
  size_t cc;
212
  for (cc = 0; cc < 3 * this->NumberOfVisibleEntries + 1; cc++) {
Daniel Pfeifer's avatar
Daniel Pfeifer committed
213
    this->Fields[cc] = CM_NULLPTR;
214
  }
215

Berk Geveci's avatar
Berk Geveci committed
216
  // Assign fields
217
  int j = 0;
218
  std::vector<cmCursesCacheEntryComposite*>::iterator it;
219
  for (it = this->Entries->begin(); it != this->Entries->end(); ++it) {
220
    const char* existingValue =
221
      this->CMakeInstance->GetState()->GetCacheEntryValue((*it)->GetValue());
222
    bool advanced =
223
224
225
      this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool(
        (*it)->GetValue(), "ADVANCED");
    if (!existingValue || (!this->AdvancedMode && advanced)) {
226
      continue;
Berk Geveci's avatar
Berk Geveci committed
227
    }
228
229
230
231
232
    this->Fields[3 * j] = (*it)->Label->Field;
    this->Fields[3 * j + 1] = (*it)->IsNewLabel->Field;
    this->Fields[3 * j + 2] = (*it)->Entry->Field;
    j++;
  }
233
  // if no cache entries there should still be one dummy field
234
  if (j == 0) {
235
    it = this->Entries->begin();
236
237
238
    this->Fields[0] = (*it)->Label->Field;
    this->Fields[1] = (*it)->IsNewLabel->Field;
    this->Fields[2] = (*it)->Entry->Field;
239
    this->NumberOfVisibleEntries = 1;
240
  }
Berk Geveci's avatar
Berk Geveci committed
241
  // Has to be null terminated.
Daniel Pfeifer's avatar
Daniel Pfeifer committed
242
  this->Fields[3 * this->NumberOfVisibleEntries] = CM_NULLPTR;
Berk Geveci's avatar
Berk Geveci committed
243
244
245
246
247
}

void cmCursesMainForm::Render(int left, int top, int width, int height)
{

248
  if (this->Form) {
Ken Martin's avatar
Ken Martin committed
249
    FIELD* currentField = current_field(this->Form);
250
251
    cmCursesWidget* cw =
      reinterpret_cast<cmCursesWidget*>(field_userptr(currentField));
Berk Geveci's avatar
Berk Geveci committed
252
    // If in edit mode, get out of it
253
254
255
    if (cw->GetType() == cmStateEnums::STRING ||
        cw->GetType() == cmStateEnums::PATH ||
        cw->GetType() == cmStateEnums::FILEPATH) {
Berk Geveci's avatar
Berk Geveci committed
256
257
      cmCursesStringWidget* sw = static_cast<cmCursesStringWidget*>(cw);
      sw->SetInEdit(false);
258
    }
Berk Geveci's avatar
Berk Geveci committed
259
    // Delete the previous form
Ken Martin's avatar
Ken Martin committed
260
261
    unpost_form(this->Form);
    free_form(this->Form);
Daniel Pfeifer's avatar
Daniel Pfeifer committed
262
    this->Form = CM_NULLPTR;
263
  }
Berk Geveci's avatar
Berk Geveci committed
264
265

  // Wrong window size
266
267
  if (width < cmCursesMainForm::MIN_WIDTH || width < this->InitialWidth ||
      height < cmCursesMainForm::MIN_HEIGHT) {
Berk Geveci's avatar
Berk Geveci committed
268
    return;
269
  }
Berk Geveci's avatar
Berk Geveci committed
270

Berk Geveci's avatar
Berk Geveci committed
271
272
  // Leave room for toolbar
  height -= 7;
Berk Geveci's avatar
Berk Geveci committed
273

274
  if (this->AdvancedMode) {
Ken Martin's avatar
Ken Martin committed
275
    this->NumberOfVisibleEntries = this->Entries->size();
276
  } else {
Berk Geveci's avatar
Berk Geveci committed
277
    // If normal, display only non-advanced entries
Ken Martin's avatar
Ken Martin committed
278
    this->NumberOfVisibleEntries = 0;
279
    std::vector<cmCursesCacheEntryComposite*>::iterator it;
280
    for (it = this->Entries->begin(); it != this->Entries->end(); ++it) {
281
      const char* existingValue =
282
        this->CMakeInstance->GetState()->GetCacheEntryValue((*it)->GetValue());
283
      bool advanced =
284
285
286
        this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool(
          (*it)->GetValue(), "ADVANCED");
      if (!existingValue || (!this->AdvancedMode && advanced)) {
287
        continue;
288
      }
289
      this->NumberOfVisibleEntries++;
290
    }
291
  }
292

Berk Geveci's avatar
Berk Geveci committed
293
  // Re-adjust the fields according to their place
Ken Martin's avatar
Ken Martin committed
294
  this->NumberOfPages = 1;
295
  if (height > 0) {
296
    bool isNewPage;
297
    int i = 0;
298
    std::vector<cmCursesCacheEntryComposite*>::iterator it;
299
    for (it = this->Entries->begin(); it != this->Entries->end(); ++it) {
300
      const char* existingValue =
301
        this->CMakeInstance->GetState()->GetCacheEntryValue((*it)->GetValue());
302
      bool advanced =
303
304
305
        this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool(
          (*it)->GetValue(), "ADVANCED");
      if (!existingValue || (!this->AdvancedMode && advanced)) {
306
        continue;
307
      }
308
309
      int row = (i % height) + 1;
      int page = (i / height) + 1;
310
      isNewPage = (page > 1) && (row == 1);
Berk Geveci's avatar
Berk Geveci committed
311

312
      if (isNewPage) {
313
        this->NumberOfPages++;
314
315
316
317
      }
      (*it)->Label->Move(left, top + row - 1, isNewPage);
      (*it)->IsNewLabel->Move(left + 32, top + row - 1, false);
      (*it)->Entry->Move(left + 33, top + row - 1, false);
318
319
      (*it)->Entry->SetPage(this->NumberOfPages);
      i++;
Berk Geveci's avatar
Berk Geveci committed
320
    }
321
  }
322

Berk Geveci's avatar
Berk Geveci committed
323
  // Post the form
Ken Martin's avatar
Ken Martin committed
324
325
  this->Form = new_form(this->Fields);
  post_form(this->Form);
Berk Geveci's avatar
Berk Geveci committed
326
  // Update toolbar
Berk Geveci's avatar
Berk Geveci committed
327
  this->UpdateStatusBar();
328
329
  this->PrintKeys();

330
  touchwin(stdscr);
Berk Geveci's avatar
Berk Geveci committed
331
332
333
  refresh();
}

Andy Cedilnik's avatar
Andy Cedilnik committed
334
void cmCursesMainForm::PrintKeys(int process /* = 0 */)
Berk Geveci's avatar
Berk Geveci committed
335
{
336
  int x, y;
337
  getmaxyx(stdscr, y, x);
338
339
  if (x < cmCursesMainForm::MIN_WIDTH || x < this->InitialWidth ||
      y < cmCursesMainForm::MIN_HEIGHT) {
Berk Geveci's avatar
Berk Geveci committed
340
    return;
341
  }
Berk Geveci's avatar
Berk Geveci committed
342
343

  // Give the current widget (if it exists), a chance to print keys
Daniel Pfeifer's avatar
Daniel Pfeifer committed
344
  cmCursesWidget* cw = CM_NULLPTR;
345
  if (this->Form) {
Ken Martin's avatar
Ken Martin committed
346
    FIELD* currentField = current_field(this->Form);
Berk Geveci's avatar
Berk Geveci committed
347
    cw = reinterpret_cast<cmCursesWidget*>(field_userptr(currentField));
348
  }
Berk Geveci's avatar
Berk Geveci committed
349

350
  if (cw) {
Ken Martin's avatar
Ken Martin committed
351
    cw->PrintKeys();
352
353
354
355
356
357
358
359
360
361
  }

  //    {
  //    }
  //  else
  //    {
  char firstLine[512] = "";
  char secondLine[512] = "";
  char thirdLine[512] = "";
  if (process) {
Nicolas Despres's avatar
Nicolas Despres committed
362
    const char* clearLine =
Nicolas Despres's avatar
Nicolas Despres committed
363
      "                                                                    ";
Nicolas Despres's avatar
Nicolas Despres committed
364
365
366
    strcpy(firstLine, clearLine);
    strcpy(secondLine, clearLine);
    strcpy(thirdLine, clearLine);
367
368
  } else {
    if (this->OkToGenerate) {
369
      sprintf(firstLine,
370
              "Press [c] to configure       Press [g] to generate and exit");
371
372
373
374
    } else {
      sprintf(firstLine,
              "Press [c] to configure                                   ");
    }
375
376
377
    {
      const char* toggleKeyInstruction =
        "Press [t] to toggle advanced mode (Currently %s)";
378
      sprintf(thirdLine, toggleKeyInstruction,
379
380
              this->AdvancedMode ? "On" : "Off");
    }
381
382
383
    sprintf(secondLine, "Press [h] for help           "
                        "Press [q] to quit without generating");
  }
Berk Geveci's avatar
Berk Geveci committed
384

385
  curses_move(y - 4, 0);
386
  char fmt_s[] = "%s";
387
  char fmt[512] = "Press [enter] to edit option Press [d] to delete an entry";
388
  if (process) {
389
    strcpy(fmt, "                           ");
390
  }
391
  printw(fmt_s, fmt);
392
  curses_move(y - 3, 0);
393
  printw(fmt_s, firstLine);
394
  curses_move(y - 2, 0);
395
  printw(fmt_s, secondLine);
396
  curses_move(y - 1, 0);
397
  printw(fmt_s, thirdLine);
Berk Geveci's avatar
Berk Geveci committed
398

399
  if (cw) {
Ken Martin's avatar
Ken Martin committed
400
    sprintf(firstLine, "Page %d of %d", cw->GetPage(), this->NumberOfPages);
401
    curses_move(0, 65 - static_cast<unsigned int>(strlen(firstLine)) - 1);
402
    printw(fmt_s, firstLine);
403
404
  }
  //    }
Berk Geveci's avatar
Berk Geveci committed
405

Ken Martin's avatar
Ken Martin committed
406
  pos_form_cursor(this->Form);
Berk Geveci's avatar
Berk Geveci committed
407
408
409
410
}

// Print the key of the current entry and the CMake version
// on the status bar. Designed for a width of 80 chars.
Andy Cedilnik's avatar
Andy Cedilnik committed
411
void cmCursesMainForm::UpdateStatusBar(const char* message)
Berk Geveci's avatar
Berk Geveci committed
412
{
413
  int x, y;
414
  getmaxyx(stdscr, y, x);
Berk Geveci's avatar
Berk Geveci committed
415
  // If window size is too small, display error and return
416
417
  if (x < cmCursesMainForm::MIN_WIDTH || x < this->InitialWidth ||
      y < cmCursesMainForm::MIN_HEIGHT) {
418
    curses_clear();
419
    curses_move(0, 0);
Bill Hoffman's avatar
Bill Hoffman committed
420
    char fmt[] = "Window is too small. A size of at least %dx%d is required.";
421
422
423
    printw(fmt, (cmCursesMainForm::MIN_WIDTH < this->InitialWidth
                   ? this->InitialWidth
                   : cmCursesMainForm::MIN_WIDTH),
424
           cmCursesMainForm::MIN_HEIGHT);
425
426
    touchwin(stdscr);
    wrefresh(stdscr);
Berk Geveci's avatar
Berk Geveci committed
427
    return;
428
  }
Berk Geveci's avatar
Berk Geveci committed
429

Berk Geveci's avatar
Berk Geveci committed
430
  // Get the key of the current entry
Ken Martin's avatar
Ken Martin committed
431
  FIELD* cur = current_field(this->Form);
432
  int findex = field_index(cur);
Daniel Pfeifer's avatar
Daniel Pfeifer committed
433
  cmCursesWidget* lbl = CM_NULLPTR;
434
435
436
437
  if (findex >= 0) {
    lbl = reinterpret_cast<cmCursesWidget*>(
      field_userptr(this->Fields[findex - 2]));
  }
438
439
  char help[128] = "";
  const char* curField = "";
440
  if (lbl) {
441
    curField = lbl->GetValue();
442

443
444
    // Get the help string of the current entry
    // and add it to the help string
445
    const char* existingValue =
446
447
448
449
450
      this->CMakeInstance->GetState()->GetCacheEntryValue(curField);
    if (existingValue) {
      const char* hs = this->CMakeInstance->GetState()->GetCacheEntryProperty(
        curField, "HELPSTRING");
      if (hs) {
451
452
        strncpy(help, hs, 127);
        help[127] = '\0';
453
      } else {
454
        help[0] = 0;
455
      }
456
457
    } else {
      sprintf(help, " ");
458
    }
459
  }
Berk Geveci's avatar
Berk Geveci committed
460

Berk Geveci's avatar
Berk Geveci committed
461
462
  // Join the key, help string and pad with spaces
  // (or truncate) as necessary
Berk Geveci's avatar
Berk Geveci committed
463
  char bar[cmCursesMainForm::MAX_WIDTH];
464
465
  size_t i, curFieldLen = strlen(curField);
  size_t helpLen = strlen(help);
466

467
  size_t width;
468
  if (x < cmCursesMainForm::MAX_WIDTH) {
469
    width = x;
470
  } else {
471
    width = cmCursesMainForm::MAX_WIDTH;
472
  }
Berk Geveci's avatar
Berk Geveci committed
473

474
  if (message) {
Andy Cedilnik's avatar
Andy Cedilnik committed
475
476
    curField = message;
    curFieldLen = strlen(message);
477
    if (curFieldLen < width) {
Andy Cedilnik's avatar
Andy Cedilnik committed
478
      strcpy(bar, curField);
479
      for (i = curFieldLen; i < width; ++i) {
480
        bar[i] = ' ';
Andy Cedilnik's avatar
Andy Cedilnik committed
481
      }
482
    } else {
Andy Cedilnik's avatar
Andy Cedilnik committed
483
      strncpy(bar, curField, width);
484
485
486
    }
  } else {
    if (curFieldLen >= width) {
Andy Cedilnik's avatar
Andy Cedilnik committed
487
      strncpy(bar, curField, width);
488
    } else {
Andy Cedilnik's avatar
Andy Cedilnik committed
489
490
      strcpy(bar, curField);
      bar[curFieldLen] = ':';
491
492
493
494
495
496
      bar[curFieldLen + 1] = ' ';
      if (curFieldLen + helpLen + 2 >= width) {
        strncpy(bar + curFieldLen + 2, help, width - curFieldLen - 2);
      } else {
        strcpy(bar + curFieldLen + 2, help);
        for (i = curFieldLen + helpLen + 2; i < width; ++i) {
497
          bar[i] = ' ';
498
        }
Berk Geveci's avatar
Berk Geveci committed
499
500
      }
    }
501
  }
Berk Geveci's avatar
Berk Geveci committed
502

503
504
  bar[width] = '\0';

Berk Geveci's avatar
Berk Geveci committed
505
  // Display CMake version info on the next line
Berk Geveci's avatar
Berk Geveci committed
506
  // We want to display this on the right
507
508
  char version[cmCursesMainForm::MAX_WIDTH];
  char vertmp[128];
509
510
511
512
513
514
  sprintf(vertmp, "CMake Version %s", cmVersion::GetCMakeVersion());
  size_t sideSpace = (width - strlen(vertmp));
  for (i = 0; i < sideSpace; i++) {
    version[i] = ' ';
  }
  sprintf(version + sideSpace, "%s", vertmp);
515
516
  version[width] = '\0';

Berk Geveci's avatar
Berk Geveci committed
517
  // Now print both lines
518
  char fmt_s[] = "%s";
519
  curses_move(y - 5, 0);
Berk Geveci's avatar
Berk Geveci committed
520
  attron(A_STANDOUT);
521
  printw(fmt_s, bar);
522
  attroff(A_STANDOUT);
523
  curses_move(y - 4, 0);
524
  printw(fmt_s, version);
Ken Martin's avatar
Ken Martin committed
525
  pos_form_cursor(this->Form);
Berk Geveci's avatar
Berk Geveci committed
526
527
}

528
void cmCursesMainForm::UpdateProgress(const char* msg, float prog, void* vp)
Andy Cedilnik's avatar
Andy Cedilnik committed
529
530
{
  cmCursesMainForm* cm = static_cast<cmCursesMainForm*>(vp);
531
  if (!cm) {
Andy Cedilnik's avatar
Andy Cedilnik committed
532
    return;
533
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
534
  char tmp[1024];
535
536
537
538
  const char* cmsg = tmp;
  if (prog >= 0) {
    sprintf(tmp, "%s %i%%", msg, (int)(100 * prog));
  } else {
Andy Cedilnik's avatar
Andy Cedilnik committed
539
    cmsg = msg;
540
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
541
542
  cm->UpdateStatusBar(cmsg);
  cm->PrintKeys(1);
543
  curses_move(1, 1);
544
  touchwin(stdscr);
Andy Cedilnik's avatar
Andy Cedilnik committed
545
546
547
  refresh();
}

548
int cmCursesMainForm::Configure(int noconfigure)
Berk Geveci's avatar
Berk Geveci committed
549
{
550
  int xi, yi;
551
  getmaxyx(stdscr, yi, xi);
552

553
  curses_move(1, 1);
Andy Cedilnik's avatar
Andy Cedilnik committed
554
555
  this->UpdateStatusBar("Configuring, please wait...");
  this->PrintKeys(1);
556
  touchwin(stdscr);
557
  refresh();
558
559
  this->CMakeInstance->SetProgressCallback(cmCursesMainForm::UpdateProgress,
                                           this);
560

Berk Geveci's avatar
Berk Geveci committed
561
562
  // always save the current gui values to disk
  this->FillCacheManagerFromUI();
563
  this->CMakeInstance->SaveCache(
Ken Martin's avatar
Ken Martin committed
564
    this->CMakeInstance->GetHomeOutputDirectory());
Daniel Pfeifer's avatar
Daniel Pfeifer committed
565
  this->LoadCache(CM_NULLPTR);
566

567
  // Get rid of previous errors
Ken Martin's avatar
Ken Martin committed
568
  this->Errors = std::vector<std::string>();
569

Berk Geveci's avatar
Berk Geveci committed
570
  // run the generate process
Ken Martin's avatar
Ken Martin committed
571
  this->OkToGenerate = true;
572
  int retVal;
573
  if (noconfigure) {
Ken Martin's avatar
Ken Martin committed
574
575
    retVal = this->CMakeInstance->DoPreConfigureChecks();
    this->OkToGenerate = false;
576
    if (retVal > 0) {
577
578
      retVal = 0;
    }
579
  } else {
Ken Martin's avatar
Ken Martin committed
580
    retVal = this->CMakeInstance->Configure();
581
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
582
  this->CMakeInstance->SetProgressCallback(CM_NULLPTR, CM_NULLPTR);
583

584
585
  keypad(stdscr, TRUE); /* Use key symbols as
                           KEY_DOWN*/
586

587
  if (retVal != 0 || !this->Errors.empty()) {
588
    // see if there was an error
589
    if (cmSystemTools::GetErrorOccuredFlag()) {
Ken Martin's avatar
Ken Martin committed
590
      this->OkToGenerate = false;
591
592
    }
    int xx, yy;
593
    getmaxyx(stdscr, yy, xx);
594
    cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(
595
596
597
      this->Errors, cmSystemTools::GetErrorOccuredFlag()
        ? "Errors occurred during the last pass."
        : "CMake produced the following output.");
598
599
    // reset error condition
    cmSystemTools::ResetErrorOccuredFlag();
600
    CurrentForm = msgs;
601
    msgs->Render(1, 1, xx, yy);
602
    msgs->HandleInput();
Berk Geveci's avatar
Berk Geveci committed
603
604
    // If they typed the wrong source directory, we report
    // an error and exit
605
    if (retVal == -2) {
Berk Geveci's avatar
Berk Geveci committed
606
      return retVal;
Berk Geveci's avatar
Berk Geveci committed
607
    }
608
609
610
    CurrentForm = this;
    this->Render(1, 1, xx, yy);
  }
611

612
  this->InitializeUI();
613
  this->Render(1, 1, xi, yi);
614

Berk Geveci's avatar
Berk Geveci committed
615
  return 0;
Berk Geveci's avatar
Berk Geveci committed
616
617
}

Ken Martin's avatar
Ken Martin committed
618
619
int cmCursesMainForm::Generate()
{
620
  int xi, yi;
621
  getmaxyx(stdscr, yi, xi);
Ken Martin's avatar
Ken Martin committed
622

623
  curses_move(1, 1);
Andy Cedilnik's avatar
Andy Cedilnik committed
624
625
  this->UpdateStatusBar("Generating, please wait...");
  this->PrintKeys(1);
Ken Martin's avatar
Ken Martin committed
626
627
  touchwin(stdscr);
  refresh();
628
629
  this->CMakeInstance->SetProgressCallback(cmCursesMainForm::UpdateProgress,
                                           this);
Ken Martin's avatar
Ken Martin committed
630
631

  // Get rid of previous errors
Ken Martin's avatar
Ken Martin committed
632
  this->Errors = std::vector<std::string>();
Ken Martin's avatar
Ken Martin committed
633
634

  // run the generate process
Ken Martin's avatar
Ken Martin committed
635
  int retVal = this->CMakeInstance->Generate();
Ken Martin's avatar
Ken Martin committed
636

Daniel Pfeifer's avatar
Daniel Pfeifer committed
637
  this->CMakeInstance->SetProgressCallback(CM_NULLPTR, CM_NULLPTR);
638
639
  keypad(stdscr, TRUE); /* Use key symbols as
                           KEY_DOWN*/
Ken Martin's avatar
Ken Martin committed
640

641
  if (retVal != 0 || !this->Errors.empty()) {
Ken Martin's avatar
Ken Martin committed
642
    // see if there was an error
643
    if (cmSystemTools::GetErrorOccuredFlag()) {
Ken Martin's avatar
Ken Martin committed
644
      this->OkToGenerate = false;
645
    }
Ken Martin's avatar
Ken Martin committed
646
647
    // reset error condition
    cmSystemTools::ResetErrorOccuredFlag();
648
    int xx, yy;
649
    getmaxyx(stdscr, yy, xx);
650
    const char* title = "Messages during last pass.";
651
    if (cmSystemTools::GetErrorOccuredFlag()) {
652
      title = "Errors occurred during the last pass.";
653
654
655
    }
    cmCursesLongMessageForm* msgs =
      new cmCursesLongMessageForm(this->Errors, title);
Ken Martin's avatar
Ken Martin committed
656
    CurrentForm = msgs;
657
    msgs->Render(1, 1, xx, yy);
Ken Martin's avatar
Ken Martin committed
658
659
660
    msgs->HandleInput();
    // If they typed the wrong source directory, we report
    // an error and exit
661
    if (retVal == -2) {
Ken Martin's avatar
Ken Martin committed
662
663
      return retVal;
    }
664
665
666
    CurrentForm = this;
    this->Render(1, 1, xx, yy);
  }
667

Ken Martin's avatar
Ken Martin committed
668
  this->InitializeUI();
669
  this->Render(1, 1, xi, yi);
670

Ken Martin's avatar
Ken Martin committed
671
672
673
  return 0;
}

674
void cmCursesMainForm::AddError(const char* message, const char* /*unused*/)
675
{
Ken Martin's avatar
Ken Martin committed
676
  this->Errors.push_back(message);
677
678
679
680
}

void cmCursesMainForm::RemoveEntry(const char* value)
{
681
  if (!value) {
682
    return;
683
  }
684
685

  std::vector<cmCursesCacheEntryComposite*>::iterator it;
686
  for (it = this->Entries->begin(); it != this->Entries->end(); ++it) {
687
    const char* val = (*it)->GetValue();
688
    if (val && !strcmp(value, val)) {
689
      this->CMakeInstance->UnwatchUnusedCli(value);
Ken Martin's avatar
Ken Martin committed
690
      this->Entries->erase(it);
691
692
      break;
    }
693
  }
694
695
}

Berk Geveci's avatar
Berk Geveci committed
696
697
// copy from the list box to the cache manager
void cmCursesMainForm::FillCacheManagerFromUI()
698
{
699
  size_t size = this->Entries->size();
700
  for (size_t i = 0; i < size; i++) {
701
    std::string cacheKey = (*this->Entries)[i]->Key;
702
703
704
    const char* existingValue =
      this->CMakeInstance->GetState()->GetCacheEntryValue(cacheKey);
    if (existingValue) {
705
      std::string oldValue = existingValue;
Ken Martin's avatar
Ken Martin committed
706
      std::string newValue = (*this->Entries)[i]->Entry->GetValue();
707
708
      std::string fixedOldValue;
      std::string fixedNewValue;
709
      cmStateEnums::CacheEntryType t =
710
        this->CMakeInstance->GetState()->GetCacheEntryType(cacheKey);
711
712
      this->FixValue(t, oldValue, fixedOldValue);
      this->FixValue(t, newValue, fixedNewValue);
713

714
      if (!(fixedOldValue == fixedNewValue)) {
715
        // The user has changed the value.  Mark it as modified.
716
717
718
719
        this->CMakeInstance->GetState()->SetCacheEntryBoolProperty(
          cacheKey, "MODIFIED", true);
        this->CMakeInstance->GetState()->SetCacheEntryValue(cacheKey,
                                                            fixedNewValue);
720
721
      }
    }
722
  }
723
724