libyui-ncurses-pkg  2.50.7
NCPkgPopupDiskspace.cc
1 /****************************************************************************
2 |
3 | Copyright (c) [2002-2011] Novell, Inc.
4 | Copyright (c) 2018 SUSE LLC
5 | All Rights Reserved.
6 |
7 | This program is free software; you can redistribute it and/or
8 | modify it under the terms of version 2 of the GNU General Public License as
9 | published by the Free Software Foundation.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program; if not, contact Novell, Inc.
18 |
19 | To contact Novell about this file by physical or electronic mail,
20 | you may find current contact information at www.novell.com
21 |
22 |***************************************************************************/
23 
24 
25 /*---------------------------------------------------------------------\
26 | |
27 | __ __ ____ _____ ____ |
28 | \ \ / /_ _/ ___|_ _|___ \ |
29 | \ V / _` \___ \ | | __) | |
30 | | | (_| |___) || | / __/ |
31 | |_|\__,_|____/ |_| |_____| |
32 | |
33 | core system |
34 | (C) SuSE GmbH |
35 \----------------------------------------------------------------------/
36 
37  File: NCPkgPopupDiskspace.cc
38 
39  Author: Gabriele Strattner <gs@suse.de>
40 
41 /-*/
42 #define YUILogComponent "ncurses-pkg"
43 #include <YUILog.h>
44 
45 #include "YMenuButton.h"
46 #include "YDialog.h"
47 #include "YTypes.h"
48 
49 #include "NCLayoutBox.h"
50 #include "NCSpacing.h"
51 #include "NCPkgStrings.h"
52 #include "NCLabel.h"
53 #include "NCPushButton.h"
54 #include "NCTable.h"
55 
56 #include "NCZypp.h"
57 
58 #include "NCPkgPopupDiskspace.h"
59 
60 #include "NCi18n.h"
61 
62 // zypp::str::form()
63 #include <zypp/base/String.h>
64 
65 // arbitrary precision integer
66 #include <boost/multiprecision/cpp_int.hpp>
67 
68 // set values as set in YQPkgDiskUsageList.cc
69 #define MIN_FREE_MB_WARN 400
70 #define MIN_FREE_MB_PROXIMITY 700
71 
72 #define MIN_PERCENT_WARN 90
73 #define MIN_PERCENT_PROXIMITY 80
74 
75 #define OVERFLOW_MB_WARN 0
76 #define OVERFLOW_MB_PROXIMITY 300
77 
78 using std::endl;
79 
80 /*
81  Textdomain "ncurses-pkg"
82 */
83 
84 namespace
85 {
86  /**
87  * Local helper method, obtain the current disk usage. Initializes the libzypp
88  * disk usage with the current values from the system if needed.
89  * @return Libzypp disk usage
90  */
91  ZyppDuSet get_du()
92  {
93  ZyppDuSet diskUsage = zypp::getZYpp()->diskUsage();
94 
95  if ( diskUsage.empty() )
96  {
97  zypp::getZYpp()->setPartitions( zypp::DiskUsageCounter::detectMountPoints() );
98  diskUsage = zypp::getZYpp()->diskUsage();
99  }
100 
101  return diskUsage;
102  }
103 
104  /**
105  * Compute used percent for the used and the total size.
106  * @param used the used size
107  * @param total the total size
108  * @return used percent (returns zero when 'total' is zero to avoid
109  * division by zero)
110  */
111  int usedPercentInt(const FSize &used, const FSize &total)
112  {
113  int percent = 0;
114 
115  if ( total != 0 )
116  percent = int(( 100 * used ) / total);
117 
118  return percent;
119  }
120 
121  /**
122  * Compute the dialog width, try to make all fields visible. Make it
123  * dependant on the longest mount point path.
124  * @return width
125  */
126  int dialogWidth()
127  {
128  int width = 0;
129  for (const ZyppPartitionDu &du: get_du())
130  {
131  if ( int(du.dir.length()) > width )
132  width = du.dir.length();
133  }
134  yuiDebug() << "The longest mount point path: " << width << " characters" << endl;
135 
136  // add the width of the other columns + small extra space
137  // (e.g. buffer for longer translations)
138  width += 50;
139 
140  // cannot be wider than the screen, keep some minimal space around the popup
141  if (width > NCurses::cols() - 6)
142  width = NCurses::cols() - 6;
143 
144  yuiDebug() << "Dialog width: " << width << endl;
145 
146  return width;
147  }
148 
149  /**
150  * Compute the dialog X position on the screen.
151  * @return X position
152  */
153  int dialogXpos()
154  {
155  return (NCurses::cols() - dialogWidth()) / 2;
156  }
157 }
158 
159  ///////////////////////////////////////////////////////////////////
160 //
161 //
162 // METHOD NAME : NCPkgDiskspace::NCPkgDiskspace
163 // METHOD TYPE : Constructor
164 //
165 // DESCRIPTION :
166 //
167 NCPkgDiskspace::NCPkgDiskspace( bool testMode )
168  : testmode( testMode )
169  , popupWin( 0 )
170 {
171 
172  if ( testMode )
173  {
174  yuiMilestone() << "TESTMODE Diskspace" << endl;
175  zypp::getZYpp()->setPartitions(zypp::DiskUsageCounter::detectMountPoints());
176  testDiskUsage = zypp::getZYpp()->diskUsage();
177  }
178 }
179 
180 ///////////////////////////////////////////////////////////////////
181 //
182 //
183 // METHOD NAME : NCPkgDiskspace::~NCPkgDiskspace
184 // METHOD TYPE : Destructor
185 //
186 // DESCRIPTION :
187 //
188 NCPkgDiskspace::~NCPkgDiskspace()
189 {
190 }
191 
192 ///////////////////////////////////////////////////////////////////
193 //
194 //
195 // METHOD NAME : NCPkgDiskspace::fillPartitionTable
196 // METHOD TYPE : void
197 //
198 // DESCRIPTION :
199 //
200 void NCPkgDiskspace::fillPartitionTable()
201 {
202  NCTable * partitions = popupWin->Partitions();
203  partitions->deleteAllItems(); // clear table
204 
205  ZyppDuSet du = get_du();
206  for (const ZyppPartitionDu &item: du)
207  {
208  if (item.readonly)
209  continue;
210 
211  FSize pkg_used (item.pkg_size, FSize::Unit::K);
212  FSize pkg_available ((item.total_size - item.pkg_size), FSize::Unit::K);
213  FSize total (item.total_size, FSize::Unit::K);
214 
215  YTableItem *newItem = new YTableItem( item.dir,
216  pkg_used.form(8),
217  pkg_available.form(8),
218  total.form(8),
219  usedPercent( pkg_used, total ) );
220 
221  partitions->addItem( newItem );
222  }
223 }
224 
225 ///////////////////////////////////////////////////////////////////
226 //
227 //
228 // METHOD NAME : NCPkgDiskspace::checkDiskSpace
229 // METHOD TYPE : std::string
230 //
231 // DESCRIPTION : called to check disk space before installation
232 // (after OK button is pressed)
233 //
234 std::string NCPkgDiskspace::checkDiskSpace()
235 {
236  ZyppDuSet diskUsage = get_du();
237 
238  std::string text = "";
239  for (const ZyppPartitionDu &du: diskUsage)
240  {
241  if (du.readonly)
242  continue;
243 
244  FSize pkg_available(du.total_size - du.pkg_size, FSize::Unit::K);
245  if ( pkg_available < 0 )
246  {
247  // make it positive
248  pkg_available *= -1;
249  text += "\"";
250  text += du.dir;
251  text += "\"";
252  text += " ";
253  text += NCPkgStrings::MoreText();
254  text += " ";
255  text += pkg_available.asString();
256  text += " ";
257  text += NCPkgStrings::MoreSpaceText();
258  text += "<br>";
259  }
260  }
261  return text;
262 }
263 
264 ///////////////////////////////////////////////////////////////////
265 //
266 //
267 // METHOD NAME : NCPkgDiskspace::checkRemainingDiskSpace
268 // METHOD TYPE : void
269 //
270 // DESCRIPTION : check whether remaining disk space enters
271 // warning or error range
272 //
273 void NCPkgDiskspace::checkRemainingDiskSpace( const ZyppPartitionDu & partition )
274 {
275  if ( partition.readonly )
276  return;
277 
278  FSize usedSize ( partition.pkg_size, FSize::Unit::K );
279  FSize totalSize ( partition.total_size, FSize::Unit::K );
280 
281  int percent = usedPercentInt(usedSize, totalSize);
282 
283  // free size in MiB
284  boost::multiprecision::cpp_int free = ( totalSize - usedSize ).in_unit(FSize::Unit::M);
285 
286  yuiMilestone() << "Partition: " << partition.dir << " Used percent: "
287  << percent << " Free: " << free << endl;
288 
289  if ( percent > MIN_PERCENT_WARN )
290  {
291  // Modern hard disks can be huge, so a warning based on percentage only
292  // can be misleading - check the absolute value, too.
293  if ( free < MIN_FREE_MB_PROXIMITY )
294  {
295  yuiWarning() << "free < MIN_FREE_MB_PROXIMITY (" << MIN_FREE_MB_PROXIMITY << ")" << endl;
296  runningOutWarning.enterProximity();
297  }
298  if ( free < MIN_FREE_MB_WARN )
299  {
300  yuiWarning() << "free < MIN_FREE_MB_WARN (" << MIN_FREE_MB_WARN << ")" << endl;
301  runningOutWarning.enterRange();
302  }
303  }
304 
305  if ( free < MIN_FREE_MB_PROXIMITY )
306  {
307  if ( percent > MIN_PERCENT_PROXIMITY )
308  runningOutWarning.enterProximity();
309  }
310 
311  if ( free < OVERFLOW_MB_WARN )
312  overflowWarning.enterRange();
313 
314  if ( free < OVERFLOW_MB_PROXIMITY )
315  overflowWarning.enterProximity();
316 
317 }
318 
319 ///////////////////////////////////////////////////////////////////
320 //
321 //
322 // METHOD NAME : NCPkgDiskspace::setDiskSpace
323 // METHOD TYPE : void
324 //
325 // DESCRIPTION : For testing only; called from NCPkgTable if the PackageSelector
326 // running in testMode
327 // TESTDESCRIPTION: Call `PackageSelector with `opt(`testMode) (ycp example).
328 // With focus on the package list press '+' or '-' to
329 // increase/decrease used diskspace (see y2log).
330 // Use the 'Actions' menu to select/delete a package.
331 //
332 void NCPkgDiskspace::setDiskSpace( wint_t ch )
333 {
334  // set diskspace values in ZyppDuSet testDiskSpace
335  for ( const ZyppPartitionDu &partitionDu: testDiskUsage )
336  {
337  FSize usedSize ( partitionDu.pkg_size, FSize::Unit::K );
338  FSize totalSize ( partitionDu.total_size, FSize::Unit::K );
339  int percent = usedPercentInt(usedSize, totalSize);
340 
341  if ( ch == '+' )
342  percent += 3;
343  else if ( ch == '-' )
344  percent -= 3;
345 
346  if ( percent < 0 )
347  percent = 0;
348 
349  partitionDu.pkg_size = partitionDu.total_size / 100 * percent;
350 
351  FSize newSize ( partitionDu.pkg_size, FSize::Unit::K );
352 
353  yuiMilestone() << "Used size (MiB): " << newSize.in_unit(FSize::Unit::M) << endl;
354  yuiMilestone() << "Total size (MiB): " << totalSize.in_unit(FSize::Unit::M) << endl;
355  }
356 }
357 
358 ///////////////////////////////////////////////////////////////////
359 //
360 //
361 // METHOD NAME : NCPkgDiskspace::checkDiskSpaceRange
362 // METHOD TYPE : void
363 //
364 // DESCRIPTION : calls checkRemaingDiskspace for every partition
365 //
366 void NCPkgDiskspace::checkDiskSpaceRange()
367 {
368  // see YQPkgDiskUsageList::updateDiskUsage()
369  runningOutWarning.clear();
370  overflowWarning.clear();
371  ZyppDuSet diskUsage;
372 
373  if ( testmode )
374  diskUsage = testDiskUsage;
375  else
376  diskUsage = zypp::getZYpp()->diskUsage();
377 
378  for (const ZyppPartitionDu &du: diskUsage)
379  {
380  // Exclude readonly dirs from the check (#384368)
381  if ( du.readonly )
382  continue;
383  checkRemainingDiskSpace( du );
384  }
385 
386  // see YQPkgDiskUsageList::postPendingWarnings()
387  if ( overflowWarning.needWarning() )
388  {
389  showInfoPopup( _( "Error: Out of disk space!" ) );
390 
391  overflowWarning.warningPostedNotify();
392  runningOutWarning.warningPostedNotify(); // Suppress this ( now redundant ) other warning
393  }
394 
395  if ( runningOutWarning.needWarning() )
396  {
397  showInfoPopup( _( "Warning: Disk space is running out!" ) );
398 
399  runningOutWarning.warningPostedNotify();
400  }
401 
402  if ( overflowWarning.leavingProximity() )
403  overflowWarning.clearHistory();
404 
405  if ( runningOutWarning.leavingProximity() )
406  runningOutWarning.clearHistory();
407 
408  if ( testmode )
409  {
410  yuiMilestone() << "Running out Warning:" << endl;
411  runningOutWarning.logSettings();
412 
413  yuiMilestone() << "Overflow Warning:" << endl;
414  overflowWarning.logSettings();
415  }
416 }
417 
418 std::string NCPkgDiskspace::usedPercent( const FSize &used, const FSize &total )
419 {
420  int percent = usedPercentInt(used, total);
421  return zypp::str::form( "%2d%%", percent );
422 }
423 
424 
425 ///////////////////////////////////////////////////////////////////
426 //
427 //
428 // METHOD NAME : NCPkgDiskspace::showInfoPopup
429 // METHOD TYPE : void
430 //
431 // DESCRIPTION :
432 //
433 void NCPkgDiskspace::showInfoPopup( std::string headline )
434 {
435 
436  popupWin = new NCPkgPopupDiskspace (wpos( (NCurses::lines() - 15)/2, dialogXpos() ), headline );
437  // update values in partition table
438  fillPartitionTable();
439  popupWin->doit();
440  YDialog::deleteTopmostDialog();
441 }
442 
443 FSize NCPkgDiskspace::calculateDiff()
444 {
445  ZyppDuSet diskUsage = get_du();
446 
447  FSize diff = 0;
448  for (const ZyppPartitionDu &du: diskUsage)
449  {
450  diff += FSize(du.pkg_size - du.used_size, FSize::Unit::K);
451  }
452 
453  return diff;
454 }
455 
456 //////////////////////////////////////////////////////////////////
457 //
458 //
459 // METHOD NAME : NCPkgPopupDiskspace::NCPkgPopupDiskspace
460 // METHOD TYPE : Constructor
461 //
462 NCPkgPopupDiskspace::NCPkgPopupDiskspace( const wpos at, std::string headline )
463  : NCPopup( at, false )
464  , partitions( 0 )
465  , okButton( 0 )
466  , head( 0 )
467 {
468  createLayout( headline );
469 }
470 
471 ///////////////////////////////////////////////////////////////////
472 //
473 //
474 // METHOD NAME : NCPkgPopupDiskspace::~NCPkgPopupDiskspace
475 // METHOD TYPE : Destructor
476 //
477 NCPkgPopupDiskspace::~NCPkgPopupDiskspace()
478 {
479 }
480 
481 //////////////////////////////////////////////////////////////////
482 //
483 //
484 // METHOD NAME : NCPkgPopupDiskspace::createLyout
485 // METHOD TYPE : void
486 //
487 // DESCRIPTION : create layout (partition table)
488 //
489 void NCPkgPopupDiskspace::createLayout( std::string headline )
490 {
491  // the vertical split is the (only) child of the dialog
492  NCLayoutBox * split = new NCLayoutBox( this, YD_VERT );
493 
494  head = new NCLabel( split, "", true, false ); // isHeading = true
495  head->setLabel( headline );
496 
497  YTableHeader * tableHeader = new YTableHeader();
498  tableHeader->addColumn( NCPkgStrings::Partition(), YAlignBegin );
499  tableHeader->addColumn( NCPkgStrings::UsedSpace(), YAlignBegin );
500  tableHeader->addColumn( NCPkgStrings::FreeSpace(), YAlignBegin );
501  tableHeader->addColumn( NCPkgStrings::TotalSpace(), YAlignBegin );
502  tableHeader->addColumn( "% ", YAlignBegin );
503 
504  // add the partition table
505  partitions = new NCTable( split, tableHeader );
506 
507  // add the ok button
508  okButton = new NCPushButton( split, NCPkgStrings::OKLabel() );
509  okButton->setFunctionKey( 10 );
510  okButton->setKeyboardFocus();
511 }
512 
513 
514 ///////////////////////////////////////////////////////////////////
515 //
516 //
517 // METHOD NAME : NCPkgPopupDiskspace::preferredWidth
518 // METHOD TYPE : int
519 //
520 int NCPkgPopupDiskspace::preferredWidth()
521 {
522  return dialogWidth();
523 }
524 
525 ///////////////////////////////////////////////////////////////////
526 //
527 //
528 // METHOD NAME : NCPkgPopupDiskspace::preferredHeight
529 // METHOD TYPE : int
530 //
531 int NCPkgPopupDiskspace::preferredHeight()
532 {
533  if ( NCurses::lines() > 15 )
534  return 15;
535  else
536  return NCurses::lines()-4;
537 }
538 
539 void NCPkgPopupDiskspace::doit()
540 {
541  postevent = NCursesEvent();
542  do
543  {
544  // show the popup
545  popupDialog();
546  } while ( postAgain() );
547 
548  popdownDialog();
549 
550 }
551 ///////////////////////////////////////////////////////////////////
552 //
553 //
554 // METHOD NAME : NCPopup::wHandleInput
555 // METHOD TYPE : NCursesEvent
556 //
557 // DESCRIPTION :
558 //
559 NCursesEvent NCPkgPopupDiskspace::wHandleInput( wint_t ch )
560 {
561  if ( ch == 27 ) // ESC
562  return NCursesEvent::cancel;
563 
564  if ( ch == KEY_RETURN )
565  return NCursesEvent::button;
566 
567  return NCDialog::wHandleInput( ch );
568 }
569 
570 ///////////////////////////////////////////////////////////////////
571 //
572 //
573 // METHOD NAME : NCPkgPopupDiskspace::postAgain
574 // METHOD TYPE : bool
575 //
576 // DESCRIPTION :
577 //
578 bool NCPkgPopupDiskspace::postAgain()
579 {
580  if ( ! postevent.widget )
581  return false;
582 
583  if ( postevent == NCursesEvent::button || postevent == NCursesEvent::cancel )
584  {
585  // return false means: close the popup dialog
586  return false;
587  }
588  return true;
589 }
590 
591 
592 
593 
595 {
596  clearHistory();
597 }
598 
599 
600 void
602 {
603  _inRange = false;
604  _hasBeenClose = _isClose;
605  _isClose = false;
606 }
607 
608 
609 void
611 {
612  clear();
613  _hasBeenClose = false;
614  _warningPosted = false;
615 }
616 
617 
618 void
620 {
621  _inRange = true;
622  enterProximity();
623 }
624 
625 
626 void
628 {
629  _isClose = true;
630  _hasBeenClose = true;
631 }
632 
633 
634 void
636 {
637  _warningPosted = true;
638 }
639 
640 
641 bool
643 {
644  return _inRange;
645 }
646 
647 
648 bool
650 {
651  return ! _isClose && ! _hasBeenClose;
652 }
653 
654 
655 bool
657 {
658  return _inRange && ! _warningPosted;
659 }
660 
661 void
662 NCPkgWarningRangeNotifier::logSettings() const
663 {
664  yuiMilestone() << "in range: " << (_inRange?"true":"false") << endl;
665  yuiMilestone() << "is close: " << (_isClose?"true":"false") << endl;
666  yuiMilestone() << "has been close: " << (_hasBeenClose?"true":"false") << endl;
667  yuiMilestone() << "warning posted: " << (_warningPosted?"true":"false") << endl;
668 }
669 
bool needWarning() const
Check if a warning should be posted, i.e.
void warningPostedNotify()
Notification that a warning has been posted.
void clearHistory()
Clear everything, including all history values such as if a warning has been posted.
bool leavingProximity() const
Check if the value is leaving the proximity range.
void clear()
Clear the current values, i.e.
NCPkgWarningRangeNotifier()
Constructor.
bool inRange() const
Check if the value is in range, i.e.
void enterProximity()
Notification that the proximity range is entered, i.e.
void enterRange()
Notification that the inner range is entered.
static const std::string OKLabel()
The label of the OK button.