libyui-ncurses-pkg  2.50.10
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 
NCPkgWarningRangeNotifier::clear
void clear()
Clear the current values, i.e.
Definition: NCPkgPopupDiskspace.cc:601
NCPkgPopupDiskspace
Definition: NCPkgPopupDiskspace.h:163
NCPkgWarningRangeNotifier::needWarning
bool needWarning() const
Check if a warning should be posted, i.e.
Definition: NCPkgPopupDiskspace.cc:656
NCPkgStrings::OKLabel
static const std::string OKLabel()
The label of the OK button.
Definition: NCPkgStrings.cc:667
NCPkgWarningRangeNotifier::warningPostedNotify
void warningPostedNotify()
Notification that a warning has been posted.
Definition: NCPkgPopupDiskspace.cc:635
NCPkgWarningRangeNotifier::enterRange
void enterRange()
Notification that the inner range is entered.
Definition: NCPkgPopupDiskspace.cc:619
NCPkgWarningRangeNotifier::clearHistory
void clearHistory()
Clear everything, including all history values such as if a warning has been posted.
Definition: NCPkgPopupDiskspace.cc:610
NCPkgWarningRangeNotifier::leavingProximity
bool leavingProximity() const
Check if the value is leaving the proximity range.
Definition: NCPkgPopupDiskspace.cc:649
NCPkgWarningRangeNotifier::NCPkgWarningRangeNotifier
NCPkgWarningRangeNotifier()
Constructor.
Definition: NCPkgPopupDiskspace.cc:594
NCPkgWarningRangeNotifier::inRange
bool inRange() const
Check if the value is in range, i.e.
Definition: NCPkgPopupDiskspace.cc:642
NCPkgWarningRangeNotifier::enterProximity
void enterProximity()
Notification that the proximity range is entered, i.e.
Definition: NCPkgPopupDiskspace.cc:627