Hello community, here is the log from the commit of package ksudoku for openSUSE:Factory checked in at 2016-01-07 00:22:45 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ksudoku (Old) and /work/SRC/openSUSE:Factory/.ksudoku.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ksudoku" Changes: -------- --- /work/SRC/openSUSE:Factory/ksudoku/ksudoku.changes 2015-11-15 12:40:07.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.ksudoku.new/ksudoku.changes 2016-01-07 00:22:46.000000000 +0100 @@ -1,0 +2,9 @@ +Sun Dec 13 13:27:32 UTC 2015 - tittiatc...@gmail.com + +- Update to KDE Applications 15.12.0 + * KDE Applications 15.12.0 + * https://www.kde.org/announcements/announce-applications-15.12.0.php + * boo#958887 + + +------------------------------------------------------------------- Old: ---- ksudoku-15.08.3.tar.xz New: ---- ksudoku-15.12.0.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ksudoku.spec ++++++ --- /var/tmp/diff_new_pack.1FgOVk/_old 2016-01-07 00:22:48.000000000 +0100 +++ /var/tmp/diff_new_pack.1FgOVk/_new 2016-01-07 00:22:48.000000000 +0100 @@ -24,7 +24,7 @@ License: GPL-2.0+ Group: Amusements/Games/Board/Puzzle Url: http://www.kde.org -Version: 15.08.3 +Version: 15.12.0 Release: 0 Source0: ksudoku-%{version}.tar.xz BuildRoot: %{_tmppath}/%{name}-%{version}-build ++++++ ksudoku-15.08.3.tar.xz -> ksudoku-15.12.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ksudoku-15.08.3/src/gui/CMakeLists.txt new/ksudoku-15.12.0/src/gui/CMakeLists.txt --- old/ksudoku-15.08.3/src/gui/CMakeLists.txt 2015-11-04 14:02:01.000000000 +0100 +++ new/ksudoku-15.12.0/src/gui/CMakeLists.txt 2015-11-05 05:20:19.000000000 +0100 @@ -9,6 +9,7 @@ symbols.cpp gamevariants.cpp welcomescreen.cpp + puzzleprinter.cpp ) set(ksudoku_views_SRCS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ksudoku-15.08.3/src/gui/gamevariants.cpp new/ksudoku-15.12.0/src/gui/gamevariants.cpp --- old/ksudoku-15.08.3/src/gui/gamevariants.cpp 2015-11-04 14:02:01.000000000 +0100 +++ new/ksudoku-15.12.0/src/gui/gamevariants.cpp 2015-11-05 05:20:19.000000000 +0100 @@ -119,38 +119,65 @@ // class GameVariantDelegate ////////////77///////////////////////////////////////////////////////////////// -GameVariantDelegate::GameVariantDelegate(QObject* parent) - : QItemDelegate(parent) +GameVariantDelegate::GameVariantDelegate(QObject* parent, QWidget * viewport) + : QItemDelegate(parent), + m_viewport(viewport) { } -QSize GameVariantDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { - Q_UNUSED(option); +QSize GameVariantDelegate::sizeHint(const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ Q_UNUSED(index); - QSize size(m_leftMargin+m_iconWidth+m_rightMargin+m_separatorPixels*2, 0); - if( m_iconHeight < option.fontMetrics.height()*3 ) - size.setHeight( option.fontMetrics.height()*3 + m_separatorPixels*3); - else - size.setHeight( m_iconHeight+m_separatorPixels*2); + // Fit a varying number of columns into the list-view. + int viewportWidth = m_viewport->width(); + int hintWidth = m_minWidth; + int nItemsPerRow = viewportWidth / m_minWidth; + + // Expand the hinted width, so that the columns will fill the viewport. + if (nItemsPerRow > 0) { + int adjustment = (viewportWidth % nItemsPerRow) ? 0 : 1; + // The adjustment works around a graphics glitch, when nItemsPerRow + // exactly divides viewportWidth and instead of nItemsPerRow columns + // we suddenly get one column less, plus a large empty space, even + // though QListView::spacing() is zero. + hintWidth = (viewportWidth - adjustment) / nItemsPerRow; + } + // Set the height to contain the icon or 4 lines of text. + QSize size (hintWidth, 0); + int fontHeight = option.fontMetrics.height(); + if (m_iconHeight < fontHeight*4) { + size.setHeight (fontHeight*4 + m_separatorPixels*3); + } + else { + size.setHeight (m_iconHeight + m_separatorPixels*2); + } return size; } void GameVariantDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { - // show background QColor bkgColor; + + // Calculate item's column num in list-view. Add 1 in odd-numbered rows. + int nItemsPerRow = qMax (m_viewport->width() / option.rect.width(), 1); + int oddColumn = index.row() % nItemsPerRow; + oddColumn = oddColumn + ((index.row() / nItemsPerRow) % 2); + if (option.state & QStyle::State_Selected) { bkgColor = option.palette.color(QPalette::Highlight); - painter->fillRect(option.rect, bkgColor); - } else if(index.row() % 2) { + } else if (oddColumn % 2) { + // The shading alternates along each row in the list view and + // each odd-numbered row starts with a shaded item, so we have + // a checkerboard pattern, regardless of whether nItemsPerRow + // is odd or even (see the above calculation). bkgColor = option.palette.color(QPalette::AlternateBase); - painter->fillRect(option.rect, bkgColor); } else { bkgColor = option.palette.color(QPalette::Base); - painter->fillRect(option.rect, bkgColor); } + painter->fillRect(option.rect, bkgColor); QRect contentRect = option.rect.adjusted(m_leftMargin, m_separatorPixels, -m_rightMargin, -m_separatorPixels); @@ -190,8 +217,10 @@ painter->setFont(previousFont); // Show Description - QString descrStr = painter->fontMetrics().elidedText(description(index), Qt::ElideRight, contentRect.width()); - painter->drawText(contentRect, descrStr); + QTextOption wrap(Qt::AlignLeft); + wrap.setWrapMode(QTextOption::WordWrap); + QString descrStr = description(index); + painter->drawText(contentRect, descrStr, wrap); painter->setPen(previousPen); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ksudoku-15.08.3/src/gui/gamevariants.h new/ksudoku-15.12.0/src/gui/gamevariants.h --- old/ksudoku-15.08.3/src/gui/gamevariants.h 2015-11-04 14:02:01.000000000 +0100 +++ new/ksudoku-15.12.0/src/gui/gamevariants.h 2015-11-05 05:20:19.000000000 +0100 @@ -102,7 +102,7 @@ }; public: - GameVariantDelegate(QObject* parent = 0); + GameVariantDelegate(QObject* parent = 0, QWidget * viewport = 0); public: QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; @@ -116,11 +116,15 @@ QString icon(const QModelIndex& index) const; bool configurable(const QModelIndex& index) const; private: + QWidget * m_viewport; + static const int m_iconWidth = 48; static const int m_iconHeight = 48; static const int m_leftMargin = 16; static const int m_rightMargin = 12; static const int m_separatorPixels = 8; + static const int m_minWidth = m_leftMargin + m_rightMargin + + 4 * (m_iconWidth + m_separatorPixels*2); }; class SudokuGame : public GameVariant { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ksudoku-15.08.3/src/gui/ksudoku.cpp new/ksudoku-15.12.0/src/gui/ksudoku.cpp --- old/ksudoku-15.08.3/src/gui/ksudoku.cpp 2015-11-04 14:02:01.000000000 +0100 +++ new/ksudoku-15.12.0/src/gui/ksudoku.cpp 2015-11-05 05:20:19.000000000 +0100 @@ -32,9 +32,6 @@ #include <QLabel> #include <QComboBox> -#include <QPrinter> -#include <QPrintDialog> - #include <KLocale> #include <KActionCollection> #include <KStandardAction> @@ -62,6 +59,8 @@ #include "skgraph.h" #include "serializer.h" +#include "puzzleprinter.h" + #include <ktar.h> #include <kstandarddirs.h> #include <kio/job.h> @@ -124,9 +123,7 @@ : KXmlGuiWindow(), m_gameVariants(new GameVariantCollection(this, true)), - m_printer(0), - m_p(0), - m_quadrant(0) + m_puzzlePrinter(0) { setObjectName( QLatin1String("ksudoku" )); @@ -172,8 +169,7 @@ KSudoku::~KSudoku() { - delete m_p; - delete m_printer; + delete m_puzzlePrinter; endCurrentGame(); } @@ -726,195 +722,17 @@ i18n("There seems to be no puzzle to print.")); return; } - SudokuType t = game.puzzle()->graph()->specificType(); - if ((t == Mathdoku) || (t == KillerSudoku)) { - KMessageBox::information (this, - i18n("Sorry, printing Mathdoku and Killer Sudoku " - "puzzles is not yet supported.")); - return; - } - sendToPrinter (game.puzzle()); -} - -void KSudoku::sendToPrinter (const Puzzle * puzzle) -{ - const SKGraph * graph = puzzle->graph(); - if (graph->sizeZ() > 1) { - KMessageBox::information (this, - i18n("Sorry, cannot print three-dimensional puzzles.")); - return; - } - const bool printMulti = Settings::printMulti(); - const int across = 2; - const int down = 2; - const QString labels = (graph->base() <= 3) ? "123456789" : - "ABCDEFGHIJKLMNOPQRSTUVWXY"; - enum Edge {Left = 0, Right, Above, Below, Detached}; - const int All = (1 << Left) + (1 << Right) + (1 << Above) + (1 << Below); - - // The printer and painter objects can persist between print requests, so - // (if required) we can print several puzzles per page and defer printing - // until the page is full or KSudoku terminates and the painter ends itself. - // NOTE: Must create painter before using functions like m_printer->width(). - if (m_printer == 0) { - m_printer = new QPrinter (QPrinter::HighResolution); - QPrintDialog * dialog = new QPrintDialog(m_printer, this); - dialog->setWindowTitle(i18n("Print Sudoku Puzzle")); - if (dialog->exec() != QDialog::Accepted) { - delete m_printer; - m_printer = 0; - return; - } - } - if (m_p == 0) { - m_p = new QPainter (m_printer); // Start a new print job. - } - - QVector<int> edges (graph->size(), 0); - int order = graph->order(); - - for (int n = 0; n < graph->cliqueCount(); n++) { - // Find out which groups are blocks of cells, not rows or columns. - QVector<int> clique = graph->clique (n); - int x = graph->cellPosX (clique.at (0)); - int y = graph->cellPosY (clique.at (0)); - bool isRow = true; - bool isCol = true; - for (int k = 1; k < order; k++) { - if (graph->cellPosX (clique.at (k)) != x) isRow = false; - if (graph->cellPosY (clique.at (k)) != y) isCol = false; - } - if (isRow || isCol) continue; // Skip rows and columns. - - // Mark the outside edges of each block. - for (int k = 0; k < order; k++) { - int cell = clique.at (k); - int x = graph->cellPosX (cell); - int y = graph->cellPosY (cell); - int nb[4] = {-1, -1, -1, -1}; - int lim = graph->sizeX() - 1; - - // Start with all edges: remove them as neighbours are found. - int edge = All; - nb[Left] = (x > 0) ? graph->cellIndex (x-1, y) : -1; - nb[Right] = (x < lim) ? graph->cellIndex (x+1, y) : -1; - nb[Above] = (y > 0) ? graph->cellIndex (x, y-1) : -1; - nb[Below] = (y < lim) ? graph->cellIndex (x, y+1) : -1; - for (int neighbour = 0; neighbour < 4; neighbour++) { - if ((nb[neighbour] < 0) || (puzzle->value(nb[neighbour]) < 0)) { - continue; // No neighbour on this side. - } - for (int cl = 0; cl < order; cl++) { - if (clique.at (cl) == nb[neighbour]) { - edge = edge - (1 << neighbour); - } - } - } - edge = (edge == All) ? (1 << Detached) : edge; - edges [cell] |= edge; // Merge the edges found for this cell. - } - } - - // Calculate the printed dimensions of the puzzle. - m_printer->setFullPage (false); // Allow minimal margins. - int pixels = qMin (m_printer->width(), m_printer->height()); - int size = pixels - (pixels / 20); // Allow about 2.5% each side. - int divs = (graph->sizeX() > 20) ? graph->sizeX() : 20; - int sCell = size / divs; // Size of each cell. - size = graph->sizeX() * sCell; // Size of the whole puzzle. - - // Check if we require more than one puzzle per page and if they would fit. - bool manyUp = printMulti && (pixels > (across * size)); - int margin1 = manyUp ? (pixels - across * size) / (across + 1) // > 1. - : (pixels - size) / 2; // = 1. - pixels = qMax (m_printer->width(), m_printer->height()); - int margin2 = manyUp ? (pixels - down * size) / (down + 1) // > 1. - : (pixels - size) / 2; // = 1. - - // Check for landscape vs. portrait mode and set the margins accordingly. - int topX = (m_printer->width() < m_printer->height())? margin1 : margin2; - int topY = (m_printer->width() < m_printer->height())? margin2 : margin1; - - if ((m_quadrant > 0) && (! manyUp)) { - bool test = m_printer->newPage(); // Page has previous output. - m_quadrant = 0; - } - topX = manyUp ? topX + (m_quadrant % across) * (topX + size) : topX; - topY = manyUp ? topY + (m_quadrant / across) * (topY + size) : topY; - m_quadrant = manyUp ? (m_quadrant + 1) : (across * down); - - // Set up parameters for the heavy and light line-drawing. - int thin = sCell / 40; // Allow 2.5%. - int thick = (thin > 0) ? 3 * thin : 3; - int nLines = graph->order() + 1; - - QPen light (QColor("#888888")); - QPen heavy (QColor(QString("black"))); - light.setWidth (thin); - heavy.setWidth (thick); - heavy.setCapStyle (Qt::RoundCap); - - // Set font size 60% height of cell. Do not draw gray lines on top of black. - QFont f = m_p->font(); - f.setPixelSize ((sCell * 6) / 10); - m_p->setFont (f); - m_p->setCompositionMode (QPainter::CompositionMode_Darken); - - // Draw each cell in the puzzle. - for (int n = 0; n < graph->size(); n++) { - if (puzzle->value (n) < 0) { - continue; // Do not draw unused cells. - } - int x = topX + sCell * graph->cellPosX (n); - int y = topY + sCell * graph->cellPosY (n); - QRect rect (x, y, sCell, sCell); - int edge = edges.at (n); - if (edge & (1 << Detached)) { // Shade a cell, as in XSudoku. - m_p->fillRect (rect, QColor ("#DDDDDD")); - } - m_p->setPen (light); // First draw every cell light. - m_p->drawRect (rect); - m_p->setPen (heavy); // Draw block boundaries heavy. - if (edge & (1<<Left)) m_p->drawLine (x, y, x, y + sCell); - if (edge & (1<<Right)) m_p->drawLine (x+sCell, y, x+sCell, y+sCell); - if (edge & (1<<Above)) m_p->drawLine (x, y, x+sCell, y); - if (edge & (1<<Below)) m_p->drawLine (x, y+sCell, x+sCell, y+sCell); - - // Draw original puzzle values heavy: filled-in/solution values light. - int v = currentGame().value (n) - 1; - if (v >= 0) { // Skip empty cells. - m_p->setPen ((puzzle->value (n) > 0) ? heavy : light); - m_p->drawText (rect, Qt::AlignCenter, labels.mid (v, 1)); - } - } - if ((! manyUp) || (m_quadrant >= (across * down))) { - endPrint(); // Print immediately. - } - else { - KMessageBox::information (this, - i18n ("The KSudoku setting for printing several puzzles per page " - "is currently selected.\n\n" - "Your puzzle will be printed when no more will fit on the " - "page or when KSudoku terminates.")); - } -} - -void KSudoku::endPrint() -{ - if (m_p != 0) { - // The current print output goes to the printer when the painter ends. - m_p->end(); - delete m_p; - m_p = 0; - m_quadrant = 0; - KMessageBox::information (this, - i18n ("KSudoku has sent output to your printer.")); + if (! m_puzzlePrinter) { + m_puzzlePrinter = new PuzzlePrinter (this); } + m_puzzlePrinter->print (game); } bool KSudoku::queryClose() { - endPrint(); + if (m_puzzlePrinter) { + m_puzzlePrinter->endPrint(); + } return true; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ksudoku-15.08.3/src/gui/ksudoku.h new/ksudoku-15.12.0/src/gui/ksudoku.h --- old/ksudoku-15.08.3/src/gui/ksudoku.h 2015-11-04 14:02:01.000000000 +0100 +++ new/ksudoku-15.12.0/src/gui/ksudoku.h 2015-11-05 05:20:19.000000000 +0100 @@ -3,6 +3,7 @@ * Copyright 2006-2007 Mick Kappenburg <ksud...@kappendburg.net> * * Copyright 2006-2007 Johannes Bergmeier <johannes.bergme...@gmx.net> * * Copyright 2012 Ian Wadham <iandw...@gmail.com> * + * Copyright 2015 Ian Wadham <iandw...@gmail.com> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -44,6 +45,7 @@ } class SKGraph; +class PuzzlePrinter; /** * This class serves as the main window for ksudoku. It handles the @@ -151,9 +153,6 @@ void adaptActions2View(); - void sendToPrinter (const ksudoku::Puzzle * puzzle); - void endPrint(); - private: QWidget* wrapper; @@ -170,9 +169,7 @@ ksudoku::GameActions* m_gameActions; - QPrinter * m_printer; - QPainter * m_p; - int m_quadrant; + PuzzlePrinter * m_puzzlePrinter; }; #endif // _KSUDOKU_H_ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ksudoku-15.08.3/src/gui/puzzleprinter.cpp new/ksudoku-15.12.0/src/gui/puzzleprinter.cpp --- old/ksudoku-15.08.3/src/gui/puzzleprinter.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/ksudoku-15.12.0/src/gui/puzzleprinter.cpp 2015-11-05 05:20:19.000000000 +0100 @@ -0,0 +1,429 @@ +/*************************************************************************** + * Copyright 2005-2007 Francesco Rossi <re...@email.it> * + * Copyright 2006-2007 Mick Kappenburg <ksud...@kappendburg.net> * + * Copyright 2006-2008 Johannes Bergmeier <johannes.bergme...@gmx.net> * + * Copyright 2012 Ian Wadham <iandw...@gmail.com> * + * Copyright 2015 Ian Wadham <iandw...@gmail.com> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "globals.h" + +#include <QPrinter> +#include <QPrintDialog> +#include <QPainter> +#include <QLine> + +#include <KLocale> + +#include "puzzle.h" +#include "skgraph.h" +#include "ksudokugame.h" +#include "puzzleprinter.h" + +#include "settings.h" + +#include <KMessageBox> + +PuzzlePrinter::PuzzlePrinter(QWidget * parent) + : + QObject(parent), + m_parent(parent), + m_printer(0), + m_p(0), + m_quadrant(0), + m_across(2), + m_down(2) +{ +} + +PuzzlePrinter::~PuzzlePrinter() +{ +} + +void PuzzlePrinter::print (const ksudoku::Game & game) +{ + const ksudoku::Puzzle * puzzle = game.puzzle(); + const SKGraph * graph = puzzle->graph(); + if (graph->sizeZ() > 1) { + KMessageBox::information (m_parent, + i18n("Sorry, cannot print three-dimensional puzzles.")); + return; + } + const int leastCellsToFit = 20; // Avoids huge cells in small puzzles. + + // Set up a QPrinter and a QPainter and allocate space on the page. + bool pageFilled = setupOutputDevices (leastCellsToFit, graph->sizeX()); + if (! m_printer) { + return; // The user did not select a printer. + } + + // Draw the puzzle grid and its contents. + bool hasBlocks = (graph->specificType() != Mathdoku); + bool hasCages = ((graph->specificType() == Mathdoku) || + (graph->specificType() == KillerSudoku)); + bool killerStyle = (graph->specificType() == KillerSudoku); + + if (hasBlocks) { // Only Mathdoku has no blocks. + drawBlocks (puzzle, graph); + } + if (hasCages) { // Mathdoku and KillerSudoku have cages. + drawCages (puzzle, graph, killerStyle); // KillerSudoku has blocks too. + } + drawValues (game, graph); // Print starting and filled-in values. + + if (pageFilled) { + endPrint(); // Print immediately. + } + else { + KMessageBox::information (m_parent, + i18n ("The KSudoku setting for printing several puzzles per page " + "is currently selected.\n\n" + "Your puzzle will be printed when no more will fit on the " + "page or when KSudoku terminates.")); + } +} + +void PuzzlePrinter::endPrint() +{ + if (m_p != 0) { + // The current print output goes to the printer when the painter ends. + m_p->end(); + delete m_p; + m_p = 0; + m_quadrant = 0; + KMessageBox::information (m_parent, + i18n ("KSudoku has sent output to your printer.")); + } +} + +bool PuzzlePrinter::setupOutputDevices (int leastCellsToFit, int puzzleWidth) +{ + // The printer and painter objects can persist between print requests, so + // (if required) we can print several puzzles per page and defer printing + // until the page is full or KSudoku terminates and the painter ends itself. + // NOTE: Must create painter before using functions like m_printer->width(). + if (m_printer == 0) { + m_printer = new QPrinter (QPrinter::HighResolution); + QPrintDialog * dialog = new QPrintDialog(m_printer, m_parent); + dialog->setWindowTitle(i18n("Print Sudoku Puzzle")); + if (dialog->exec() != QDialog::Accepted) { + delete m_printer; + m_printer = 0; + return false; + } + } + if (m_p == 0) { + m_p = new QPainter (m_printer); // Start a new print job. + } + m_printMulti = Settings::printMulti(); + + // Calculate the printed dimensions of the puzzle. + m_printer->setFullPage (false); // Allow minimal margins. + int pixels = qMin (m_printer->width(), m_printer->height()); + int space = pixels - (pixels / 20); // Allow about 2.5% each side. + int pCells = qMax (leastCellsToFit, puzzleWidth); // Cells to allocate. + m_sCell = space / pCells; // Size of each cell. + int size = puzzleWidth * m_sCell; // Size of the whole puzzle. + + // Check if we require more than one puzzle per page and if they would fit. + bool manyUp = m_printMulti && (pixels > (m_across * size)); + int margin1 = manyUp ? (pixels - m_across * size) / (m_across + 1) // > 1. + : (pixels - size) / 2; // = 1. + pixels = qMax (m_printer->width(), m_printer->height()); + int margin2 = manyUp ? (pixels - m_down * size) / (m_down + 1) // > 1. + : (pixels - size) / 2; // = 1. + + // Check for landscape vs. portrait mode and set the margins accordingly. + m_topX = (m_printer->width() < m_printer->height())? margin1 : margin2; + m_topY = (m_printer->width() < m_printer->height())? margin2 : margin1; + + // If new puzzle will not fit a quadrant and page is not empty, flush page. + if ((m_quadrant > 0) && (! manyUp)) { + m_printer->newPage(); + m_quadrant = 0; + } + m_topX = manyUp ? m_topX + (m_quadrant%m_across) * (m_topX + size) : m_topX; + m_topY = manyUp ? m_topY + (m_quadrant/m_across) * (m_topY + size) : m_topY; + m_quadrant = manyUp ? (m_quadrant + 1) : (m_across * m_down); + + // Set up pen-parameters for the heavy and light line-drawing. + int thin = m_sCell / 40; // Allow 2.5%. + int thick = (thin > 0) ? 2 * thin : 2; + + m_light.setColor (QColor("#888888")); + m_light.setWidth (thin); + m_heavy.setColor (QColor(QString("black"))); + m_heavy.setWidth (thick); + m_heavy.setCapStyle (Qt::RoundCap); + m_dashes.setColor (QColor(QString("black"))); + m_dashes.setWidth (thin); + m_dashes.setStyle (Qt::DashLine); + + // Return true if the page will be filled up after drawing the puzzle. + return ((! manyUp) || (m_quadrant >= (m_across * m_down))); +} + +void PuzzlePrinter::drawBlocks (const ksudoku::Puzzle * puzzle, + const SKGraph * graph) +{ + QVector<int> edges (graph->size(), 0); // One bitmap per cell. + int order = graph->order(); + + for (int n = 0; n < graph->cliqueCount(); n++) { + // Find out which groups are blocks of cells, not rows or columns. + QVector<int> clique = graph->clique (n); + int x = graph->cellPosX (clique.at (0)); + int y = graph->cellPosY (clique.at (0)); + bool isRow = true; + bool isCol = true; + for (int k = 1; k < order; k++) { + if (graph->cellPosX (clique.at (k)) != x) isRow = false; + if (graph->cellPosY (clique.at (k)) != y) isCol = false; + } + if (isRow || isCol) continue; // Skip rows and columns. + + // Mark the outside edges of each block. + markEdges (clique, puzzle, graph, edges); + } + + // Draw each cell in the puzzle. + for (int n = 0; n < graph->size(); n++) { + if (puzzle->value (n) < 0) { + continue; // Do not draw unused cells. + } + drawCell (graph->cellPosX (n), graph->cellPosY (n), edges.at (n)); + } +} + +void PuzzlePrinter::drawCages (const ksudoku::Puzzle * puzzle, + const SKGraph * graph, bool killerStyle) +{ + QVector<int> edges (graph->size(), 0); // One bitmap per cell. + for (int n = 0; n < graph->cageCount(); n++) { + // Mark the outside edges of each cage. + markEdges (graph->cage (n), puzzle, graph, edges); + } + if (killerStyle) { + drawKillerSudokuCages (graph, edges); + } + else { + // Draw each cell in the puzzle. + for (int n = 0; n < graph->size(); n++) { + if (puzzle->value (n) < 0) { + continue; // Do not draw unused cells. + } + drawCell (graph->cellPosX (n), graph->cellPosY (n), edges.at (n)); + } + } + for (int n = 0; n < graph->cageCount(); n++) { + drawCageLabel (graph, n, killerStyle); + } +} + +void PuzzlePrinter::markEdges (const QVector<int> & cells, + const ksudoku::Puzzle * puzzle, + const SKGraph * graph, QVector<int> & edges) +{ + const int All = (1 << Left) + (1 << Right) + (1 << Above) + (1 << Below); + + int nCells = cells.count(); + for (int k = 0; k < nCells; k++) { + int cell = cells.at (k); + int x = graph->cellPosX (cell); + int y = graph->cellPosY (cell); + int nb[4] = {-1, -1, -1, -1}; + int lim = graph->sizeX() - 1; + + // Start with all edges: remove them as neighbours are found. + int edge = All; + nb[Left] = (x > 0) ? graph->cellIndex (x-1, y) : -1; + nb[Right] = (x < lim) ? graph->cellIndex (x+1, y) : -1; + nb[Above] = (y > 0) ? graph->cellIndex (x, y-1) : -1; + nb[Below] = (y < lim) ? graph->cellIndex (x, y+1) : -1; + for (int neighbour = 0; neighbour < 4; neighbour++) { + if ((nb[neighbour] < 0) || (puzzle->value(nb[neighbour]) < 0)) { + continue; // No neighbour on this side. + } + for (int cl = 0; cl < nCells; cl++) { + if (cells.at (cl) == nb[neighbour]) { + edge = edge - (1 << neighbour); + } + } + } + // Check for size-1 cages or detached cells as in XSudoku diagonals. + if ((edge == All) && (graph->specificType() != Mathdoku) && + (graph->specificType() != KillerSudoku)) { + edge = (1 << Detached); + } + edges [cell] |= edge; // Merge the edges found for this cell. + } +} + +void PuzzlePrinter::drawCell (int posX, int posY, int edge) +{ + int x = m_topX + m_sCell * posX; + int y = m_topY + m_sCell * posY; + QRect rect (x, y, m_sCell, m_sCell); + if (edge & (1 << Detached)) { // Shade a cell, as in XSudoku. + m_p->fillRect (rect, QColor ("#DDDDDD")); + } + m_p->setPen (m_light); // First draw every cell light. + m_p->drawRect (rect); + m_p->setPen (m_heavy); // Draw block boundaries heavy. + if (edge & (1<<Left)) m_p->drawLine (x, y, x, y + m_sCell); + if (edge & (1<<Right)) m_p->drawLine (x+m_sCell, y, x+m_sCell, y+m_sCell); + if (edge & (1<<Above)) m_p->drawLine (x, y, x+m_sCell, y); + if (edge & (1<<Below)) m_p->drawLine (x, y+m_sCell, x+m_sCell, y+m_sCell); +} + +void PuzzlePrinter::drawValues (const ksudoku::Game& game, const SKGraph* graph) +{ + const QString labels = (graph->base() <= 3) ? "123456789" : + "ABCDEFGHIJKLMNOPQRSTUVWXY"; + // Set font size 60% height of cell. + QFont f = m_p->font(); + f.setPixelSize ((m_sCell * 6) / 10); + m_p->setFont (f); + + for (int n = 0; n < graph->size(); n++) { + int v = game.value (n) - 1; + if (v < 0) { + continue; // Skip empty or unused cells. + } + + // Draw original puzzle values heavy: filled-in/solution values light. + int x = m_topX + m_sCell * graph->cellPosX (n); + int y = m_topY + m_sCell * graph->cellPosY (n); + QRect rect (x, y, m_sCell, m_sCell); + m_p->setPen (game.given (n) ? m_heavy : m_light); + m_p->drawText (rect, Qt::AlignCenter, labels.mid (v, 1)); + } +} + +void PuzzlePrinter::drawKillerSudokuCages (const SKGraph* graph, + const QVector<int> & edges) +{ + // Killer Sudokus have cages AND groups: so the cages are drawn differently. + // We keep the outer wall of the cage on our left and draw a dashed line + // just inside that boundary. This reduces ugly criss-crossing of lines. + // + // Directions and related arrays are all in clockwise order. + enum Direction {East, South, West, North, nDirections}; + const Direction rightTurn [nDirections] = {South, West, North, East}; + const Direction leftTurn [nDirections] = {North, East, South, West}; + const int wallOnLeft [nDirections] = + {1 << Above, 1 << Right, 1 << Below, 1 << Left}; + const int wallAhead [nDirections] = + {1 << Right, 1 << Below, 1 << Left, 1 << Above}; + + const int deltaX [nDirections] = {+1, 0, -1, 0}; + const int deltaY [nDirections] = {0, +1, 0, -1}; + + int cellInc [nDirections] = {graph->order(), +1, -graph->order(), -1}; + int offset = (m_sCell + 6) / 12; + int longSide = m_sCell; + int shortSide = m_sCell - offset - offset; + + m_p->setPen (m_dashes); + + for (int n = 0; n < graph->cageCount(); n++) { + int topLeft = graph->cageTopLeft(n); + int cell = topLeft; + int edge = edges.at (cell); + int startX = m_topX + m_sCell * graph->cellPosX (cell) + offset; + int startY = m_topY + m_sCell * graph->cellPosY (cell) + offset; + int dx = 0; + int dy = 0; + QLine line (startX, startY, startX, startY); + Direction direction = East; + + // Keep drawing until we get back to the starting cell and direction. + do { + // If there is a wall on the left, follow it. + if (edge & wallOnLeft [direction]) { + if (edge & wallAhead [direction]) { + // Go to wall (shortSide), draw line, turn right, new line. + dx = deltaX [direction] * shortSide; + dy = deltaY [direction] * shortSide; + line.setLine (line.x1(), line.y1(), + line.x2() + dx, line.y2() + dy); + m_p->drawLine (line); + direction = rightTurn [direction]; + line.setLine (line.x2(), line.y2(), line.x2(), line.y2()); + } + else { + // Extend to start of next cell (longSide). + dx = deltaX [direction] * longSide; + dy = deltaY [direction] * longSide; + line.setLine (line.x1(), line.y1(), + line.x2() + dx, line.y2() + dy); + cell = cell + cellInc [direction]; + edge = edges.at (cell); + } + } + // Else, if there is no wall on the left ... + else { + // Draw line, turn left, new line, go to start of next cell. + m_p->drawLine (line); + direction = leftTurn [direction]; + dx = deltaX [direction] * (longSide - shortSide); + dy = deltaY [direction] * (longSide - shortSide); + line.setLine (line.x2(), line.y2(), + line.x2() + dx, line.y2() + dy); + cell = cell + cellInc [direction]; + edge = edges.at (cell); + + } + } while (! ((cell == topLeft) && (direction == East))); + } // Draw next cage. +} + +void PuzzlePrinter::drawCageLabel (const SKGraph* graph, int n, + bool killerStyle) +{ + if (graph->cage (n).size() < 2) { + return; + } + + int topLeft = graph->cageTopLeft (n); + int cellX = m_topX + m_sCell * graph->cellPosX (topLeft); + int cellY = m_topY + m_sCell * graph->cellPosY (topLeft); + + QString cLabel = QString::number (graph->cageValue (n)); + if (! killerStyle) { // No operator is shown in KillerSudoku. + cLabel = cLabel + QString(" /-x+").mid(graph->cageOperator (n), 1); + } + + QFont f = m_p->font(); + f.setPixelSize ((m_sCell + 3)/4); + f.setBold (true); + m_p->setFont(f); + QFontMetrics fm(f); + int w = fm.width(cLabel); + int a = fm.ascent(); + int m = (fm.width(QChar('1'))+1)/3; // Left margin = 1/3 width of '1'. + + if (killerStyle) { + // Cover part of the dashed line, to make a background for the text. + m_p->fillRect(cellX + m, cellY + m, w + w/10, a /* h */, Qt::white); + } + // Note: Origin of text is on baseline to left of first character. + m_p->drawText (cellX + m, cellY + a, cLabel); +} + +#include "puzzleprinter.moc" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ksudoku-15.08.3/src/gui/puzzleprinter.h new/ksudoku-15.12.0/src/gui/puzzleprinter.h --- old/ksudoku-15.08.3/src/gui/puzzleprinter.h 1970-01-01 01:00:00.000000000 +0100 +++ new/ksudoku-15.12.0/src/gui/puzzleprinter.h 2015-11-05 05:20:19.000000000 +0100 @@ -0,0 +1,88 @@ +/*************************************************************************** + * Copyright 2005-2007 Francesco Rossi <re...@email.it> * + * Copyright 2006-2007 Mick Kappenburg <ksud...@kappendburg.net> * + * Copyright 2006-2007 Johannes Bergmeier <johannes.bergme...@gmx.net> * + * Copyright 2012 Ian Wadham <iandw...@gmail.com> * + * Copyright 2015 Ian Wadham <iandw...@gmail.com> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef _PUZZLEPRINTER_H_ +#define _PUZZLEPRINTER_H_ + +#include <QPen> + +namespace ksudoku { +class Game; +class Puzzle; +} + +class SKGraph; +class QWidget; + +class PuzzlePrinter : public QObject +{ + Q_OBJECT + +public: + /** + * Default Constructor + */ + PuzzlePrinter (QWidget * parent); + + /** + * Default Destructor + */ + virtual ~PuzzlePrinter(); + + void print (const ksudoku::Game & game); + void endPrint(); + +private: + enum Edge {Left = 0, Right, Above, Below, Detached}; + + bool setupOutputDevices (int leastCellsToFit, int puzzleWidth); + + void drawBlocks (const ksudoku::Puzzle * puzzle, + const SKGraph * graph); + void drawCages (const ksudoku::Puzzle * puzzle, + const SKGraph * graph, bool killerStyle); + void drawKillerSudokuCages (const SKGraph* graph, + const QVector<int> & edges); + void markEdges (const QVector<int> & cells, + const ksudoku::Puzzle * puzzle, const SKGraph * graph, + QVector<int> & edges); + void drawCell (int posX, int posY, int edge); + void drawValues (const ksudoku::Game & game, const SKGraph * graph); + void drawCageLabel (const SKGraph* graph, int n, bool killerStyle); + + QWidget * m_parent; + QPrinter * m_printer; + QPainter * m_p; + int m_quadrant; + int m_across; + int m_down; + bool m_printMulti; + int m_sCell; + int m_topX; + int m_topY; + QPen m_heavy; + QPen m_light; + QPen m_dashes; +}; + +#endif // _PUZZLEPRINTER_H_ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ksudoku-15.08.3/src/gui/welcomescreen.cpp new/ksudoku-15.12.0/src/gui/welcomescreen.cpp --- old/ksudoku-15.08.3/src/gui/welcomescreen.cpp 2015-11-04 14:02:01.000000000 +0100 +++ new/ksudoku-15.12.0/src/gui/welcomescreen.cpp 2015-11-05 05:20:19.000000000 +0100 @@ -34,9 +34,24 @@ WelcomeScreen::WelcomeScreen(QWidget* parent, GameVariantCollection* collection) : QFrame(parent), m_collection(collection) { - QItemDelegate* delegate = new GameVariantDelegate(this); - - setupUi(this); + setupUi(this); // Get gameListWidget by loading from welcomescreen.ui. + + // Set the screen to display a multi-column list of puzzle-types, with + // vertical scrolling. GameVariantDelegate::sizeHint() calculates the + // number of columns and their width when it works out the size of the + // item's display-area. + + QItemDelegate* delegate = + new GameVariantDelegate(this, gameListWidget->viewport()); + gameListWidget->setWrapping(true); + gameListWidget->setResizeMode(QListView::Adjust); + gameListWidget->setUniformItemSizes(true); + gameListWidget->setFlow(QListView::LeftToRight); + + // Avoid a resize loop (with the scrollbar appearing and disappearing) + // if ever the number of items and display-columns hits a bad combo. + gameListWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + gameListWidget->setModel(m_collection); gameListWidget->setItemDelegate(delegate); gameListWidget->setVerticalScrollMode(QListView::ScrollPerPixel); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ksudoku-15.08.3/src/shapes/Killer_4x4.desktop new/ksudoku-15.12.0/src/shapes/Killer_4x4.desktop --- old/ksudoku-15.08.3/src/shapes/Killer_4x4.desktop 2015-11-04 14:02:01.000000000 +0100 +++ new/ksudoku-15.12.0/src/shapes/Killer_4x4.desktop 2015-11-05 05:20:19.000000000 +0100 @@ -17,7 +17,6 @@ Name[sr@ijekavianlatin]=Sićušni ubica Name[sr@latin]=Sićušni ubica Name[sv]=Liten mördare -Name[tr]=Minik Killer Name[uk]=Малий кілер Name[x-test]=xxTiny Killerxx Description=4x4 Sudoku, but cages must add to totals shown @@ -38,7 +37,6 @@ Description[sr@ijekavianlatin]=Sudoku 4×4, ali kavezi moraju da dobiju prikazane zbirove Description[sr@latin]=Sudoku 4×4, ali kavezi moraju da dobiju prikazane zbirove Description[sv]=4x4 Sudoku, men avgränsningar måste adderas till visade summor -Description[tr]=4x4 Sudoku, ama kafesler gösterilen toplamlara eklemek zorunda Description[uk]=Судоку 4x4, але блоки мають складати сумі показане число Description[x-test]=xx4x4 Sudoku, but cages must add to totals shownxx Author=Ian Wadham diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ksudoku-15.08.3/src/shapes/Killer_9x9.desktop new/ksudoku-15.12.0/src/shapes/Killer_9x9.desktop --- old/ksudoku-15.08.3/src/shapes/Killer_9x9.desktop 2015-11-04 14:02:01.000000000 +0100 +++ new/ksudoku-15.12.0/src/shapes/Killer_9x9.desktop 2015-11-05 05:20:19.000000000 +0100 @@ -18,7 +18,6 @@ Name[sr@ijekavianlatin]=Sudoku ubica Name[sr@latin]=Sudoku ubica Name[sv]=Mördarsudoku -Name[tr]=Killer Sudoku Name[uk]=Судоку-кілер Name[x-test]=xxKiller Sudokuxx Description=Classic Sudoku, but cages must add to totals shown @@ -39,7 +38,6 @@ Description[sr@ijekavianlatin]=Klasični sudoku, ali kavezi moraju da dobiju prikazane zbirove Description[sr@latin]=Klasični sudoku, ali kavezi moraju da dobiju prikazane zbirove Description[sv]=Klassisk Sudoku, men avgränsningar måste adderas till visade summor -Description[tr]=Klasik Sudoku, ama kafesler gösterilen toplamlara eklemek zorunda Description[uk]=Класичне судоку, але блоки мають складати у сумі показане число Description[x-test]=xxClassic Sudoku, but cages must add to totals shownxx Author=Ian Wadham diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ksudoku-15.08.3/src/shapes/Mathdoku_4x4.desktop new/ksudoku-15.12.0/src/shapes/Mathdoku_4x4.desktop --- old/ksudoku-15.08.3/src/shapes/Mathdoku_4x4.desktop 2015-11-04 14:02:01.000000000 +0100 +++ new/ksudoku-15.12.0/src/shapes/Mathdoku_4x4.desktop 2015-11-05 05:20:19.000000000 +0100 @@ -18,7 +18,6 @@ Name[sr@ijekavianlatin]=Matudoku 101 Name[sr@latin]=Matudoku 101 Name[sv]=Mattedoku 101 -Name[tr]=Matematikdoku 101 Name[uk]=Матдоку 101 Name[x-test]=xxMathdoku 101xx Description=Size 4x4 grid, with calculated cages @@ -39,7 +38,6 @@ Description[sr@ijekavianlatin]=Mreža 4×4, sa izračunatim kavezima Description[sr@latin]=Mreža 4×4, sa izračunatim kavezima Description[sv]=Storlek 4x4 med beräknade avgränsningar -Description[tr]=Boyut 4x4, hesaplanan kafeslerle Description[uk]=Ґратка розміром 4x4, із обчисленими блоками Description[x-test]=xxSize 4x4 grid, with calculated cagesxx Author=Ian Wadham diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ksudoku-15.08.3/src/shapes/Mathdoku_Settable.desktop new/ksudoku-15.12.0/src/shapes/Mathdoku_Settable.desktop --- old/ksudoku-15.08.3/src/shapes/Mathdoku_Settable.desktop 2015-11-04 14:02:01.000000000 +0100 +++ new/ksudoku-15.12.0/src/shapes/Mathdoku_Settable.desktop 2015-11-05 05:20:19.000000000 +0100 @@ -18,7 +18,6 @@ Name[sr@ijekavianlatin]=Matudoku — podesiva veličina Name[sr@latin]=Matudoku — podesiva veličina Name[sv]=Mattedoku - Inställningsbar storlek -Name[tr]=Matematikdoku - Ayarlanabilir Boyut Name[uk]=Матдоку — змінний розмір Name[x-test]=xxMathdoku - Settable Sizexx Description=Size 3x3 to 9x9 grid, with calculated cages @@ -39,7 +38,6 @@ Description[sr@ijekavianlatin]=Mreža od 3×3 do 9×9, sa izračunatim kavezima Description[sr@latin]=Mreža od 3×3 do 9×9, sa izračunatim kavezima Description[sv]=Storlek 3x3 till 9x9 med beräknade avgränsningar -Description[tr]=Boyut 3x3 ile 9x9 arasında, hesaplanan kafeslerle Description[uk]=Ґратка розміром від 3x3 до 9x9, із обчисленими блоками Description[x-test]=xxSize 3x3 to 9x9 grid, with calculated cagesxx Author=Ian Wadham