libyui-ncurses-pkg  2.48.9
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  // show the popup
544  popupDialog( );
545  } while ( postAgain() );
546 
547  popdownDialog();
548 
549 }
550 ///////////////////////////////////////////////////////////////////
551 //
552 //
553 // METHOD NAME : NCPopup::wHandleInput
554 // METHOD TYPE : NCursesEvent
555 //
556 // DESCRIPTION :
557 //
558 NCursesEvent NCPkgPopupDiskspace::wHandleInput( wint_t ch )
559 {
560  if ( ch == 27 ) // ESC
561  return NCursesEvent::cancel;
562 
563  if ( ch == KEY_RETURN )
564  return NCursesEvent::button;
565 
566  return NCDialog::wHandleInput( ch );
567 }
568 
569 ///////////////////////////////////////////////////////////////////
570 //
571 //
572 // METHOD NAME : NCPkgPopupDiskspace::postAgain
573 // METHOD TYPE : bool
574 //
575 // DESCRIPTION :
576 //
577 bool NCPkgPopupDiskspace::postAgain()
578 {
579  if ( ! postevent.widget )
580  return false;
581 
582  if ( postevent == NCursesEvent::button || postevent == NCursesEvent::cancel )
583  {
584  // return false means: close the popup dialog
585  return false;
586  }
587  return true;
588 }
589 
590 
591 
592 
594 {
595  clearHistory();
596 }
597 
598 
599 void
601 {
602  _inRange = false;
603  _hasBeenClose = _isClose;
604  _isClose = false;
605 }
606 
607 
608 void
610 {
611  clear();
612  _hasBeenClose = false;
613  _warningPosted = false;
614 }
615 
616 
617 void
619 {
620  _inRange = true;
621  enterProximity();
622 }
623 
624 
625 void
627 {
628  _isClose = true;
629  _hasBeenClose = true;
630 }
631 
632 
633 void
635 {
636  _warningPosted = true;
637 }
638 
639 
640 bool
642 {
643  return _inRange;
644 }
645 
646 
647 bool
649 {
650  return ! _isClose && ! _hasBeenClose;
651 }
652 
653 
654 bool
656 {
657  return _inRange && ! _warningPosted;
658 }
659 
660 void
661 NCPkgWarningRangeNotifier::logSettings() const
662 {
663  yuiMilestone() << "in range: " << (_inRange?"true":"false") << endl;
664  yuiMilestone() << "is close: " << (_isClose?"true":"false") << endl;
665  yuiMilestone() << "has been close: " << (_hasBeenClose?"true":"false") << endl;
666  yuiMilestone() << "warning posted: " << (_warningPosted?"true":"false") << endl;
667 }
668 
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.