From 5d13a4812a43512fbed76aef46a7e00d983f5716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 8 Oct 2012 07:38:34 +0200 Subject: [PATCH] a?adido soporte para la configuraci?n del comic flow a?adido soporte para la configuraci?n de la calidad del comic flow primera versi?n del di?logo de configuraci?n del servidor implementada --- YACReaderLibrary/images.qrc | 3 + YACReaderLibrary/library_window.cpp | 18 +- YACReaderLibrary/library_window.h | 3 + YACReaderLibrary/options_dialog.cpp | 614 ++++++++++++++++++---- YACReaderLibrary/options_dialog.h | 74 ++- YACReaderLibrary/server/startup.cpp | 6 +- YACReaderLibrary/server_config_dialog.cpp | 82 ++- YACReaderLibrary/server_config_dialog.h | 5 +- common/comic_flow_widget.cpp | 82 ++- common/comic_flow_widget.h | 21 + common/custom_widgets.cpp | 55 +- common/custom_widgets.h | 23 + common/pictureflow.h | 423 +++++++-------- common/yacreader_flow_gl.cpp | 167 ++++-- common/yacreader_flow_gl.h | 35 +- images/iphoneConfig.png | Bin 0 -> 16872 bytes images/qrMessage.png | Bin 0 -> 2781 bytes images/server.png | Bin 0 -> 14333 bytes 18 files changed, 1220 insertions(+), 391 deletions(-) create mode 100644 images/iphoneConfig.png create mode 100644 images/qrMessage.png create mode 100644 images/server.png diff --git a/YACReaderLibrary/images.qrc b/YACReaderLibrary/images.qrc index 8658670d..6ce0c2f7 100644 --- a/YACReaderLibrary/images.qrc +++ b/YACReaderLibrary/images.qrc @@ -38,5 +38,8 @@ ../images/db.png ../images/asignNumber.png ../images/defaultCover.png + ../images/server.png + ../images/iphoneConfig.png + ../images/qrMessage.png \ No newline at end of file diff --git a/YACReaderLibrary/library_window.cpp b/YACReaderLibrary/library_window.cpp index 03b9cdd9..ce53016b 100644 --- a/YACReaderLibrary/library_window.cpp +++ b/YACReaderLibrary/library_window.cpp @@ -34,6 +34,9 @@ void LibraryWindow::setupUI() libraryCreator = new LibraryCreator(); packageManager = new PackageManager(); + settings = new QSettings("YACReaderLibrary.ini",QSettings::IniFormat); //TODO unificar la creación del fichero de config con el servidor + settings->beginGroup("libraryConfig"); + doModels(); doDialogs(); doLayout(); @@ -51,8 +54,12 @@ void LibraryWindow::doLayout() QSplitter * sHorizontal = new QSplitter(Qt::Horizontal); //spliter principal //TODO: flowType is a global variable //CONFIG COMIC_FLOW-------------------------------------------------------- - comicFlow = new ComicFlowWidgetGL(0); - comicFlow->setFlowType(flowType); + if(settings->contains("useOpenGL") && settings->value("useOpenGL").toBool() == true) + comicFlow = new ComicFlowWidgetGL(0); + else + comicFlow = new ComicFlowWidgetSW(0); + //comicFlow->setFlowType(flowType); + comicFlow->updateConfig(settings); comicFlow->setFocusPolicy(Qt::StrongFocus); comicFlow->setShowMarks(true); QMatrix m; @@ -186,7 +193,7 @@ void LibraryWindow::doDialogs() importComicsInfoDialog = new ImportComicsInfoDialog(this); addLibraryDialog = new AddLibraryDialog(this); optionsDialog = new OptionsDialog(this); - optionsDialog->restoreOptions(); + optionsDialog->restoreOptions(settings); serverConfigDialog = new ServerConfigDialog(this); had = new HelpAboutDialog(this); //TODO load data. @@ -330,7 +337,7 @@ void LibraryWindow::createActions() serverConfigAction = new QAction(this); serverConfigAction->setToolTip(tr("Show comics server options dialog")); - serverConfigAction->setIcon(QIcon(":/images/options.png")); + serverConfigAction->setIcon(QIcon(":/images/server.png")); //disable actions updateLibraryAction->setEnabled(false); @@ -1262,7 +1269,8 @@ void LibraryWindow::importLibrary(QString clc,QString destPath,QString name) void LibraryWindow::reloadOptions() { - comicFlow->setFlowType(flowType); + //comicFlow->setFlowType(flowType); + comicFlow->updateConfig(settings); } //TODO esto sobra diff --git a/YACReaderLibrary/library_window.h b/YACReaderLibrary/library_window.h index f3857ceb..92fff750 100644 --- a/YACReaderLibrary/library_window.h +++ b/YACReaderLibrary/library_window.h @@ -157,6 +157,9 @@ private: void enableLibraryActions(); QString currentPath(); + + //settings + QSettings * settings; public: LibraryWindow(); public slots: diff --git a/YACReaderLibrary/options_dialog.cpp b/YACReaderLibrary/options_dialog.cpp index 0744b59f..a5f544df 100644 --- a/YACReaderLibrary/options_dialog.cpp +++ b/YACReaderLibrary/options_dialog.cpp @@ -8,13 +8,33 @@ #include #include #include +#include + +#include "custom_widgets.h" +#include "yacreader_flow_gl.h" PictureFlow::FlowType flowType = PictureFlow::Strip; OptionsDialog::OptionsDialog(QWidget * parent) :QDialog() { - QVBoxLayout * layout = new QVBoxLayout(this); + QVBoxLayout * layout = new QVBoxLayout; + QLayout * layout1 = setupLayoutSW(); + QLayout * layout2 = setupLayoutGL(); + + sw = new QWidget(this); + layout1->setContentsMargins(0,0,0,0); + sw->setLayout(layout1); + + gl = new QWidget(this); + layout2->setContentsMargins(0,0,0,0); + gl->setLayout(layout2); + + QHBoxLayout * switchFlowType = new QHBoxLayout; + switchFlowType->addStretch(); + switchFlowType->addWidget(useGL = new QCheckBox(tr("Use hardware acceleration (restart needed)"))); + + connect(useGL,SIGNAL(stateChanged(int)),this,SLOT(saveUseGL(int))); accept = new QPushButton(tr("Save")); cancel = new QPushButton(tr("Cancel")); @@ -22,127 +42,525 @@ OptionsDialog::OptionsDialog(QWidget * parent) connect(cancel,SIGNAL(clicked()),this,SLOT(restoreOptions())); connect(cancel,SIGNAL(clicked()),this,SLOT(close())); - QGroupBox *groupBox = new QGroupBox(tr("How to show covers:")); - - radio1 = new QRadioButton(tr("CoverFlow look")); - radio2 = new QRadioButton(tr("Stripe look")); - radio3 = new QRadioButton(tr("Overlapped Stripe look")); - - - QVBoxLayout *vbox = new QVBoxLayout; - QHBoxLayout * opt1 = new QHBoxLayout; - opt1->addWidget(radio1); - QLabel * lOpt1 = new QLabel(); - lOpt1->setPixmap(QPixmap(":/images/flow1.png")); - opt1->addStretch(); - opt1->addWidget(lOpt1); - vbox->addLayout(opt1); - - QHBoxLayout * opt2 = new QHBoxLayout; - opt2->addWidget(radio2); - QLabel * lOpt2 = new QLabel(); - lOpt2->setPixmap(QPixmap(":/images/flow2.png")); - opt2->addStretch(); - opt2->addWidget(lOpt2); - vbox->addLayout(opt2); - - QHBoxLayout * opt3 = new QHBoxLayout; - opt3->addWidget(radio3); - QLabel * lOpt3 = new QLabel(); - lOpt3->setPixmap(QPixmap(":/images/flow3.png")); - opt3->addStretch(); - opt3->addWidget(lOpt3); - vbox->addLayout(opt3); - - - //vbox->addStretch(1); - groupBox->setLayout(vbox); - QHBoxLayout * buttons = new QHBoxLayout(); buttons->addStretch(); buttons->addWidget(accept); buttons->addWidget(cancel); - layout->addWidget(groupBox); + layout->addWidget(sw); + layout->addWidget(gl); + layout->addLayout(switchFlowType); layout->addLayout(buttons); - setLayout(layout); + sw->hide(); - restoreOptions(); //load options - resize(200,0); + setLayout(layout); + //restoreOptions(settings); //load options + //resize(200,0); setModal (true); setWindowTitle("Options"); } -void OptionsDialog::findFolder() +QLayout * OptionsDialog::setupLayoutSW() { - QString s = QFileDialog::getExistingDirectory(0,tr("Comics directory"),"."); - if(!s.isEmpty()) - { - pathEdit->setText(s); - } + QVBoxLayout * layout = new QVBoxLayout(this); + + QGroupBox *groupBox = new QGroupBox(tr("How to show covers:")); + + radio1 = new QRadioButton(tr("CoverFlow look")); + radio2 = new QRadioButton(tr("Stripe look")); + radio3 = new QRadioButton(tr("Overlapped Stripe look")); + + + QVBoxLayout *vbox = new QVBoxLayout; + QHBoxLayout * opt1 = new QHBoxLayout; + opt1->addWidget(radio1); + QLabel * lOpt1 = new QLabel(); + lOpt1->setPixmap(QPixmap(":/images/flow1.png")); + opt1->addStretch(); + opt1->addWidget(lOpt1); + vbox->addLayout(opt1); + + QHBoxLayout * opt2 = new QHBoxLayout; + opt2->addWidget(radio2); + QLabel * lOpt2 = new QLabel(); + lOpt2->setPixmap(QPixmap(":/images/flow2.png")); + opt2->addStretch(); + opt2->addWidget(lOpt2); + vbox->addLayout(opt2); + + QHBoxLayout * opt3 = new QHBoxLayout; + opt3->addWidget(radio3); + QLabel * lOpt3 = new QLabel(); + lOpt3->setPixmap(QPixmap(":/images/flow3.png")); + opt3->addStretch(); + opt3->addWidget(lOpt3); + vbox->addLayout(opt3); + + + //vbox->addStretch(1); + groupBox->setLayout(vbox); + + layout->addWidget(groupBox); + + return layout; } +QLayout * OptionsDialog::setupLayoutGL() +{ + QVBoxLayout * layout = new QVBoxLayout(this); + + //PRESETS------------------------------------------------------------------ + QGroupBox *groupBox = new QGroupBox(tr("Presets:")); + + radioClassic = new QRadioButton(tr("Classic look")); + connect(radioClassic,SIGNAL(toggled(bool)),this,SLOT(setClassicConfig())); + + radioStripe = new QRadioButton(tr("Stripe look")); + connect(radioStripe,SIGNAL(toggled(bool)),this,SLOT(setStripeConfig())); + + radioOver = new QRadioButton(tr("Overlapped Stripe look")); + connect(radioOver,SIGNAL(toggled(bool)),this,SLOT(setOverlappedStripeConfig())); + + radionModern = new QRadioButton(tr("Modern look")); + connect(radionModern,SIGNAL(toggled(bool)),this,SLOT(setModernConfig())); + + radioDown = new QRadioButton(tr("Roulette look")); + connect(radioDown,SIGNAL(toggled(bool)),this,SLOT(setRouletteConfig())); + + QVBoxLayout *vbox = new QVBoxLayout; + QHBoxLayout * opt1 = new QHBoxLayout; + opt1->addWidget(radioClassic); + QLabel * lOpt1 = new QLabel(); + lOpt1->setPixmap(QPixmap(":/images/flow1.png")); + opt1->addStretch(); + opt1->addWidget(lOpt1); + vbox->addLayout(opt1); + + QHBoxLayout * opt2 = new QHBoxLayout; + opt2->addWidget(radioStripe); + QLabel * lOpt2 = new QLabel(); + lOpt2->setPixmap(QPixmap(":/images/flow2.png")); + opt2->addStretch(); + opt2->addWidget(lOpt2); + vbox->addLayout(opt2); + + QHBoxLayout * opt3 = new QHBoxLayout; + opt3->addWidget(radioOver); + QLabel * lOpt3 = new QLabel(); + lOpt3->setPixmap(QPixmap(":/images/flow3.png")); + opt3->addStretch(); + opt3->addWidget(lOpt3); + vbox->addLayout(opt3); + + QHBoxLayout * opt4 = new QHBoxLayout; + opt4->addWidget(radionModern); + QLabel * lOpt4 = new QLabel(); + lOpt4->setPixmap(QPixmap(":/images/flow3.png")); + opt4->addStretch(); + opt4->addWidget(lOpt4); + vbox->addLayout(opt4); + + QHBoxLayout * opt5 = new QHBoxLayout; + opt5->addWidget(radioDown); + QLabel * lOpt5 = new QLabel(); + lOpt5->setPixmap(QPixmap(":/images/flow3.png")); + opt5->addStretch(); + opt5->addWidget(lOpt5); + vbox->addLayout(opt5); + + groupBox->setLayout(vbox); + + //OPTIONS------------------------------------------------------------------ + QGroupBox *optionsGroupBox = new QGroupBox(tr("Custom:")); + + xRotation = new YACReaderSpinSliderWidget(this); + xRotation->setText(tr("View angle")); + xRotation->setRange(0,90); + connect(xRotation,SIGNAL(valueChanged(int)),this,SIGNAL(optionsChanged())); + connect(xRotation,SIGNAL(valueChanged(int)),this,SLOT(saveXRotation(int))); + + yPosition = new YACReaderSpinSliderWidget(this); + yPosition->setText(tr("Position")); + yPosition->setRange(-100,100); + connect(yPosition,SIGNAL(valueChanged(int)),this,SIGNAL(optionsChanged())); + connect(yPosition,SIGNAL(valueChanged(int)),this,SLOT(saveYPosition(int))); + + coverDistance = new YACReaderSpinSliderWidget(this); + coverDistance->setText(tr("Cover gap")); + coverDistance->setRange(0,150); + connect(coverDistance,SIGNAL(valueChanged(int)),this,SIGNAL(optionsChanged())); + connect(coverDistance,SIGNAL(valueChanged(int)),this,SLOT(saveCoverDistance(int))); + + centralDistance = new YACReaderSpinSliderWidget(this); + centralDistance->setText(tr("Central gap")); + centralDistance->setRange(0,150); + connect(centralDistance,SIGNAL(valueChanged(int)),this,SIGNAL(optionsChanged())); + connect(centralDistance,SIGNAL(valueChanged(int)),this,SLOT(saveCentralDistance(int))); + + zoomLevel = new YACReaderSpinSliderWidget(this); + zoomLevel->setText(tr("Zoom")); + zoomLevel->setRange(-20,0); + connect(zoomLevel,SIGNAL(valueChanged(int)),this,SIGNAL(optionsChanged())); + connect(zoomLevel,SIGNAL(valueChanged(int)),this,SLOT(saveZoomLevel(int))); + + yCoverOffset = new YACReaderSpinSliderWidget(this); + yCoverOffset->setText(tr("Y offset")); + yCoverOffset->setRange(-50,50); + connect(yCoverOffset,SIGNAL(valueChanged(int)),this,SIGNAL(optionsChanged())); + connect(yCoverOffset,SIGNAL(valueChanged(int)),this,SLOT(saveYCoverOffset(int))); + + zCoverOffset = new YACReaderSpinSliderWidget(this); + zCoverOffset->setText(tr("Z offset")); + zCoverOffset->setRange(-50,50); + connect(zCoverOffset,SIGNAL(valueChanged(int)),this,SIGNAL(optionsChanged())); + connect(zCoverOffset,SIGNAL(valueChanged(int)),this,SLOT(saveZCoverOffset(int))); + + coverRotation = new YACReaderSpinSliderWidget(this); + coverRotation->setText(tr("Cover Angle")); + coverRotation->setRange(0,360); + connect(coverRotation,SIGNAL(valueChanged(int)),this,SIGNAL(optionsChanged())); + connect(coverRotation,SIGNAL(valueChanged(int)),this,SLOT(saveCoverRotation(int))); + + fadeOutDist = new YACReaderSpinSliderWidget(this); + fadeOutDist->setText(tr("Visibility")); + fadeOutDist->setRange(0,10); + connect(fadeOutDist,SIGNAL(valueChanged(int)),this,SIGNAL(optionsChanged())); + connect(fadeOutDist,SIGNAL(valueChanged(int)),this,SLOT(saveFadeOutDist(int))); + + lightStrength = new YACReaderSpinSliderWidget(this); + lightStrength->setText(tr("Light")); + lightStrength->setRange(-10,10); + connect(lightStrength,SIGNAL(valueChanged(int)),this,SIGNAL(optionsChanged())); + connect(lightStrength,SIGNAL(valueChanged(int)),this,SLOT(saveLightStrength(int))); + + maxAngle = new YACReaderSpinSliderWidget(this); + maxAngle->setText(tr("Max angle")); + maxAngle->setRange(0,90); + connect(maxAngle,SIGNAL(valueChanged(int)),this,SIGNAL(optionsChanged())); + connect(maxAngle,SIGNAL(valueChanged(int)),this,SLOT(saveMaxAngle(int))); + + QVBoxLayout *optionsLayoutStretch = new QVBoxLayout; + optionsLayoutStretch->setContentsMargins(0,0,0,0); + QGridLayout *optionsLayout = new QGridLayout; + optionsLayout->addWidget(xRotation,0,0); + optionsLayout->addWidget(yPosition,0,1); + optionsLayout->addWidget(coverDistance,1,0); + optionsLayout->addWidget(centralDistance,1,1); + optionsLayout->addWidget(zoomLevel,2,0); + optionsLayout->addWidget(yCoverOffset,2,1); + optionsLayout->addWidget(zCoverOffset,3,0); + optionsLayout->addWidget(coverRotation,3,1); + optionsLayout->addWidget(fadeOutDist,4,0); + optionsLayout->addWidget(lightStrength,4,1); + optionsLayout->addWidget(maxAngle,5,0); + + optionsLayoutStretch->addLayout(optionsLayout); + optionsLayoutStretch->addStretch(); + + optionsGroupBox->setLayout(optionsLayoutStretch); + + QHBoxLayout * groupBoxesLayout = new QHBoxLayout; + groupBoxesLayout->addWidget(groupBox); + groupBoxesLayout->addWidget(optionsGroupBox); + + QHBoxLayout * performance = new QHBoxLayout; + performance->addWidget(new QLabel(tr("Low Performance"))); + performance->addWidget(performanceSlider = new QSlider(Qt::Horizontal)); + performance->addWidget(new QLabel(tr("High Performance"))); + + performanceSlider->setMinimum(0); + performanceSlider->setMaximum(3); + performanceSlider->setSingleStep(1); + performanceSlider->setPageStep(1); + performanceSlider->setTickInterval(1); + performanceSlider->setTickPosition(QSlider::TicksRight); + + connect(performanceSlider, SIGNAL(valueChanged(int)),this,SLOT(savePerformance(int))); + connect(performanceSlider, SIGNAL(valueChanged(int)),this,SLOT(optionsChanged())); + + + layout->addLayout(groupBoxesLayout); + layout->addLayout(performance); + + return layout; +} + +void OptionsDialog::savePerformance(int value) +{ + settings->setValue("performance",value); +} + +void OptionsDialog::saveUseGL(int b) +{ + if(Qt::Checked == b) + { + sw->setVisible(false); + gl->setVisible(true); + } + else + { + gl->setVisible(false); + sw->setVisible(true); + } + resize(0,0); + settings->setValue("useOpenGL",b); +} + +void OptionsDialog::saveXRotation(int value) +{ + settings->setValue("flowType",PictureFlow::Custom); + settings->setValue("xRotation",xRotation->getValue()); +} +void OptionsDialog::saveYPosition(int value) +{ + settings->setValue("flowType",PictureFlow::Custom); + settings->setValue("yPosition",yPosition->getValue()); +} +void OptionsDialog::saveCoverDistance(int value) +{ + settings->setValue("flowType",PictureFlow::Custom); + settings->setValue("coverDistance",coverDistance->getValue()); +} +void OptionsDialog::saveCentralDistance(int value) +{ + settings->setValue("flowType",PictureFlow::Custom); + settings->setValue("centralDistance",centralDistance->getValue()); +} +void OptionsDialog::saveZoomLevel(int value) +{ + settings->setValue("flowType",PictureFlow::Custom); + settings->setValue("zoomLevel",zoomLevel->getValue()); +} +void OptionsDialog::saveYCoverOffset(int value) +{ + settings->setValue("flowType",PictureFlow::Custom); + settings->setValue("yCoverOffset",yCoverOffset->getValue()); +} +void OptionsDialog::saveZCoverOffset(int value) +{ + settings->setValue("flowType",PictureFlow::Custom); + settings->setValue("zCoverOffset",zCoverOffset->getValue()); +} +void OptionsDialog::saveCoverRotation(int value) +{ + settings->setValue("flowType",PictureFlow::Custom); + settings->setValue("coverRotation",coverRotation->getValue()); +} +void OptionsDialog::saveFadeOutDist(int value) +{ + settings->setValue("flowType",PictureFlow::Custom); + settings->setValue("fadeOutDist",fadeOutDist->getValue()); +} +void OptionsDialog::saveLightStrength(int value) +{ + settings->setValue("flowType",PictureFlow::Custom); + settings->setValue("lightStrength",lightStrength->getValue()); +} + +void OptionsDialog::saveMaxAngle(int value) +{ + settings->setValue("flowType",PictureFlow::Custom); + settings->setValue("maxAngle",maxAngle->getValue()); +} + void OptionsDialog::saveOptions() { - QFile f(QCoreApplication::applicationDirPath()+"/YACReaderLibrary.conf"); - if(!f.open(QIODevice::WriteOnly)) - { - QMessageBox::critical(NULL,tr("Saving config file...."),tr("There was a problem saving YACReaderLibrary configuration. Please, check if you have enough permissions in the YACReader root folder.")); - } - else - { - QTextStream txtS(&f); - if(radio1->isChecked()) - { - txtS << "FLOW_TYPE" << "\n" << (int)PictureFlow::CoverFlowLike << "\n"; - flowType = PictureFlow::CoverFlowLike; - } - if(radio2->isChecked()) - { - txtS << "FLOW_TYPE" << "\n" << (int)PictureFlow::Strip << "\n"; - flowType = PictureFlow::Strip; - } - if(radio3->isChecked()) - { - txtS << "FLOW_TYPE" << "\n" << (int)PictureFlow::StripOverlapped << "\n"; - flowType = PictureFlow::StripOverlapped; - } - f.close(); - close(); emit(optionsChanged()); - } + close(); } -void OptionsDialog::restoreOptions() +void OptionsDialog::restoreOptions(QSettings * settings) { - QFile f(QCoreApplication::applicationDirPath()+"/YACReaderLibrary.conf"); - if(f.exists()) + this->settings = settings; + + if(settings->contains("useOpenGL") && settings->value("useOpenGL").toInt() == Qt::Checked) { - f.open(QIODevice::ReadOnly); - QTextStream txtS(&f); - QString content = txtS.readAll(); + sw->setVisible(false); + gl->setVisible(true); + useGL->setChecked(true); + } + else + { + gl->setVisible(false); + sw->setVisible(true); + useGL->setChecked(false); + } + + + if(!settings->contains("flowType")) + { + setClassicConfig(); + radioClassic->setChecked(true); + performanceSlider->setValue(1); + return; + } + + performanceSlider->setValue(settings->value("performance").toInt()); + PictureFlow::FlowType flowType; + switch(settings->value("flowType").toInt()) + { + case 0: + flowType = PictureFlow::CoverFlowLike; + break; + case 1: + flowType = PictureFlow::Strip; + break; + case 2: + flowType = PictureFlow::StripOverlapped; + break; + case 3: + flowType = PictureFlow::Modern; + break; + case 4: + flowType = PictureFlow::Roulette; + break; + case 5: + flowType = PictureFlow::Custom; + break; + } - QStringList lines = content.split('\n'); - if(lines.count()>0){ - QString name = lines.at(1); - switch(flowType=(PictureFlow::FlowType)name.toInt()){ - case PictureFlow::CoverFlowLike: - radio1->setChecked(true); - break; - case PictureFlow::Strip: - radio2->setChecked(true); - break; - case PictureFlow::StripOverlapped: - radio3->setChecked(true); - break; + if(flowType == PictureFlow::Custom) + { + loadConfig(); + return; } - } - else - flowType=PictureFlow::Strip; - } - else - flowType=PictureFlow::Strip; + if(flowType == PictureFlow::CoverFlowLike) + { + setClassicConfig(); + radioClassic->setChecked(true); + return; + } + + if(flowType == PictureFlow::Strip) + { + setStripeConfig(); + radioStripe->setChecked(true); + return; + } + + if(flowType == PictureFlow::StripOverlapped) + { + setOverlappedStripeConfig(); + radioOver->setChecked(true); + return; + } + + if(flowType == PictureFlow::Modern) + { + setModernConfig(); + radionModern->setChecked(true); + return; + } + + if(flowType == PictureFlow::Roulette) + { + setRouletteConfig(); + radioDown->setChecked(true); + return; + } +} + +void OptionsDialog::loadConfig() +{ + + xRotation->setValue(settings->value("xRotation").toInt()); + yPosition->setValue(settings->value("yPosition").toInt()); + coverDistance->setValue(settings->value("coverDistance").toInt()); + centralDistance->setValue(settings->value("centralDistance").toInt()); + zoomLevel->setValue(settings->value("zoomLevel").toInt()); + yCoverOffset->setValue(settings->value("yCoverOffset").toInt()); + zCoverOffset->setValue(settings->value("zCoverOffset").toInt()); + coverRotation->setValue(settings->value("coverRotation").toInt()); + fadeOutDist->setValue(settings->value("fadeOutDist").toInt()); + lightStrength->setValue(settings->value("lightStrength").toInt()); + maxAngle->setValue(settings->value("maxAngle").toInt()); +} +void OptionsDialog::setClassicConfig() +{ + + settings->setValue("flowType",PictureFlow::CoverFlowLike); + + xRotation->setValue(presetYACReaderFlowClassicConfig.cfRX); + yPosition->setValue(presetYACReaderFlowClassicConfig.cfY*100); + coverDistance->setValue(presetYACReaderFlowClassicConfig.xDistance*100); + centralDistance->setValue(presetYACReaderFlowClassicConfig.centerDistance*100); + zoomLevel->setValue(presetYACReaderFlowClassicConfig.cfZ); + yCoverOffset->setValue(presetYACReaderFlowClassicConfig.yDistance*100); + zCoverOffset->setValue(presetYACReaderFlowClassicConfig.zDistance*100); + coverRotation->setValue(presetYACReaderFlowClassicConfig.rotation*-1); + fadeOutDist->setValue(presetYACReaderFlowClassicConfig.animationFadeOutDist); + lightStrength->setValue(presetYACReaderFlowClassicConfig.viewRotateLightStrenght); + maxAngle->setValue(presetYACReaderFlowClassicConfig.viewAngle); +} + +void OptionsDialog::setStripeConfig() +{ + + settings->setValue("flowType",PictureFlow::Strip); + + xRotation->setValue(presetYACReaderFlowStripeConfig.cfRX); + yPosition->setValue(presetYACReaderFlowStripeConfig.cfY*100); + coverDistance->setValue(presetYACReaderFlowStripeConfig.xDistance*100); + centralDistance->setValue(presetYACReaderFlowStripeConfig.centerDistance*100); + zoomLevel->setValue(presetYACReaderFlowStripeConfig.cfZ); + yCoverOffset->setValue(presetYACReaderFlowStripeConfig.yDistance*100); + zCoverOffset->setValue(presetYACReaderFlowStripeConfig.zDistance*100); + coverRotation->setValue(presetYACReaderFlowStripeConfig.rotation*-1); + fadeOutDist->setValue(presetYACReaderFlowStripeConfig.animationFadeOutDist); + lightStrength->setValue(presetYACReaderFlowStripeConfig.viewRotateLightStrenght); + maxAngle->setValue(presetYACReaderFlowStripeConfig.viewAngle); +} + +void OptionsDialog::setOverlappedStripeConfig() +{ + settings->setValue("flowType",PictureFlow::StripOverlapped); + + xRotation->setValue(presetYACReaderFlowOverlappedStripeConfig.cfRX); + yPosition->setValue(presetYACReaderFlowOverlappedStripeConfig.cfY*100); + coverDistance->setValue(presetYACReaderFlowOverlappedStripeConfig.xDistance*100); + centralDistance->setValue(presetYACReaderFlowOverlappedStripeConfig.centerDistance*100); + zoomLevel->setValue(presetYACReaderFlowOverlappedStripeConfig.cfZ); + yCoverOffset->setValue(presetYACReaderFlowOverlappedStripeConfig.yDistance*100); + zCoverOffset->setValue(presetYACReaderFlowOverlappedStripeConfig.zDistance*100); + coverRotation->setValue(presetYACReaderFlowOverlappedStripeConfig.rotation*-1); + fadeOutDist->setValue(presetYACReaderFlowOverlappedStripeConfig.animationFadeOutDist); + lightStrength->setValue(presetYACReaderFlowOverlappedStripeConfig.viewRotateLightStrenght); + maxAngle->setValue(presetYACReaderFlowOverlappedStripeConfig.viewAngle); +} + +void OptionsDialog::setModernConfig() +{ + settings->setValue("flowType",PictureFlow::Modern); + + xRotation->setValue(defaultYACReaderFlowConfig.cfRX); + yPosition->setValue(defaultYACReaderFlowConfig.cfY*100); + coverDistance->setValue(defaultYACReaderFlowConfig.xDistance*100); + centralDistance->setValue(defaultYACReaderFlowConfig.centerDistance*100); + zoomLevel->setValue(defaultYACReaderFlowConfig.cfZ); + yCoverOffset->setValue(defaultYACReaderFlowConfig.yDistance*100); + zCoverOffset->setValue(defaultYACReaderFlowConfig.zDistance*100); + coverRotation->setValue(defaultYACReaderFlowConfig.rotation*-1); + fadeOutDist->setValue(defaultYACReaderFlowConfig.animationFadeOutDist); + lightStrength->setValue(defaultYACReaderFlowConfig.viewRotateLightStrenght); + maxAngle->setValue(defaultYACReaderFlowConfig.viewAngle); +} + +void OptionsDialog::setRouletteConfig() +{ + settings->setValue("flowType",PictureFlow::Roulette); + + xRotation->setValue(pressetYACReaderFlowDownConfig.cfRX); + yPosition->setValue(pressetYACReaderFlowDownConfig.cfY*100); + coverDistance->setValue(pressetYACReaderFlowDownConfig.xDistance*100); + centralDistance->setValue(pressetYACReaderFlowDownConfig.centerDistance*100); + zoomLevel->setValue(pressetYACReaderFlowDownConfig.cfZ); + yCoverOffset->setValue(pressetYACReaderFlowDownConfig.yDistance*100); + zCoverOffset->setValue(pressetYACReaderFlowDownConfig.zDistance*100); + coverRotation->setValue(pressetYACReaderFlowDownConfig.rotation*-1); + fadeOutDist->setValue(pressetYACReaderFlowDownConfig.animationFadeOutDist); + lightStrength->setValue(pressetYACReaderFlowDownConfig.viewRotateLightStrenght); + maxAngle->setValue(pressetYACReaderFlowDownConfig.viewAngle); } diff --git a/YACReaderLibrary/options_dialog.h b/YACReaderLibrary/options_dialog.h index 36fe2d6a..4d631a15 100644 --- a/YACReaderLibrary/options_dialog.h +++ b/YACReaderLibrary/options_dialog.h @@ -8,8 +8,12 @@ #include #include #include +#include +#include #include "pictureflow.h" +#include "custom_widgets.h" + extern PictureFlow::FlowType flowType; class OptionsDialog : public QDialog @@ -18,32 +22,74 @@ Q_OBJECT public: OptionsDialog(QWidget * parent = 0); private: - QLabel * pathLabel; - QLineEdit * pathEdit; - QPushButton * pathFindButton; - - QLabel * magGlassSizeLabel; - - QLabel * zoomLevel; - - QLabel * slideSizeLabel; - QSlider * slideSize; - + QPushButton * accept; QPushButton * cancel; + QCheckBox * useGL; + //SW......................... QRadioButton *radio1; QRadioButton *radio2; QRadioButton *radio3; + //GL......................... + QRadioButton *radioClassic; + QRadioButton *radioStripe; + QRadioButton *radioOver; + QRadioButton *radionModern; + QRadioButton *radioDown; + + YACReaderSpinSliderWidget * xRotation; + YACReaderSpinSliderWidget * yPosition; + YACReaderSpinSliderWidget * coverDistance; + YACReaderSpinSliderWidget * centralDistance; + YACReaderSpinSliderWidget * zoomLevel; + YACReaderSpinSliderWidget * yCoverOffset; + YACReaderSpinSliderWidget * zCoverOffset; + YACReaderSpinSliderWidget * coverRotation; + YACReaderSpinSliderWidget * fadeOutDist; + YACReaderSpinSliderWidget * lightStrength; + YACReaderSpinSliderWidget * maxAngle; + + QSlider * performanceSlider; + + QWidget * sw; + QWidget * gl; + + QLayout * setupLayoutSW(); + QLayout * setupLayoutGL(); + + QSettings * settings; + QSettings * previousSettings; + + private slots: + void savePerformance(int value); + void saveUseGL(int b); + void saveXRotation(int value); + void saveYPosition(int value); + void saveCoverDistance(int value); + void saveCentralDistance(int value); + void saveZoomLevel(int value); + void saveYCoverOffset(int value); + void saveZCoverOffset(int value); + void saveCoverRotation(int value); + void saveFadeOutDist(int value); + void saveLightStrength(int value); + void saveMaxAngle(int value); + void loadConfig(); + void setClassicConfig(); + void setStripeConfig(); + void setOverlappedStripeConfig(); + void setModernConfig(); + void setRouletteConfig(); public slots: void saveOptions(); - void restoreOptions(); - void findFolder(); - + void restoreOptions(QSettings * settings); signals: void optionsChanged(); + + }; diff --git a/YACReaderLibrary/server/startup.cpp b/YACReaderLibrary/server/startup.cpp index 2cb3a60e..b8599dc7 100644 --- a/YACReaderLibrary/server/startup.cpp +++ b/YACReaderLibrary/server/startup.cpp @@ -16,16 +16,16 @@ #define APPNAME "YACReaderLibrary" /** Publisher of this application */ -#define ORGANISATION "Butterfly" +#define ORGANISATION "YACReader" /** Short description of this application */ -#define DESCRIPTION "Web service based on Qt" +#define DESCRIPTION "Comic reader and organizer" void Startup::start() { // Initialize the core application QCoreApplication* app = QApplication::instance(); app->setApplicationName(APPNAME); - //app->setOrganizationName(ORGANISATION); + app->setOrganizationName(ORGANISATION); QString configFileName=Static::getConfigDir()+"/"+QCoreApplication::applicationName()+".ini"; // Configure logging into files diff --git a/YACReaderLibrary/server_config_dialog.cpp b/YACReaderLibrary/server_config_dialog.cpp index d374a230..32036e87 100644 --- a/YACReaderLibrary/server_config_dialog.cpp +++ b/YACReaderLibrary/server_config_dialog.cpp @@ -17,12 +17,60 @@ ServerConfigDialog::ServerConfigDialog(QWidget * parent) qrCodeImage = new QPixmap(); qrCode = new QLabel("xxxx",this); - QGridLayout * mainLayout = new QGridLayout; - mainLayout->addWidget(accept,0,0); - mainLayout->addWidget(qrCode,0,1); + + + + + QGridLayout * gridEdits = new QGridLayout; + gridEdits->addWidget(new QLabel(tr("IP")),0,0); + gridEdits->addWidget(new QLabel(tr("Port")),0,1); + ip = new QLineEdit(". . ."); + gridEdits->addWidget(ip,1,0); + port = new QLineEdit("8080"); + port->setMaximumWidth(50); + gridEdits->addWidget(port,1,1); + gridEdits->setColumnStretch(0,1); + gridEdits->setColumnStretch(1,0); + + QHBoxLayout * codeLayout = new QHBoxLayout; + codeLayout->addStretch(); + QLabel * qrMessage = new QLabel(); + qrMessage->setPixmap(QPixmap(":/images/qrMessage.png")); + codeLayout->addWidget(qrMessage); + codeLayout->addWidget(qrCode); + + QVBoxLayout * configLayout = new QVBoxLayout; + configLayout->addLayout(gridEdits); + configLayout->addLayout(codeLayout); + configLayout->addStretch(); + configLayout->setSpacing(5); + + QHBoxLayout * elementsLayout = new QHBoxLayout; + + QLabel * iphone = new QLabel(); + iphone->setPixmap(QPixmap(":/images/iphoneConfig.png")); + elementsLayout->setSpacing(40); + elementsLayout->addWidget(iphone); + elementsLayout->addStretch(); + elementsLayout->addLayout(configLayout); + + QHBoxLayout * buttons = new QHBoxLayout; + buttons->addStretch(); + buttons->addWidget(accept); + + QVBoxLayout * mainLayout = new QVBoxLayout; + mainLayout->addLayout(elementsLayout); + mainLayout->addLayout(buttons); + //mainLayout->addWidget(qrCode,0,1); this->setLayout(mainLayout); generateQR(); + + QPalette Pal(palette()); + // set black background + Pal.setColor(QPalette::Background, Qt::white); + setAutoFillBackground(true); + setPalette(Pal); } void ServerConfigDialog::generateQR() @@ -50,12 +98,17 @@ void ServerConfigDialog::generateQR() } } if(!dir.isEmpty()) + { generateQR(dir+":"+s->getPort()); + ip->setText(dir); + port->setText(s->getPort()); + } else { } //qrCode->setText(dir+":8080"); + } @@ -63,7 +116,7 @@ void ServerConfigDialog::generateQR(const QString & serverAddress) { qrGenerator = new QProcess(); QStringList attributes; - attributes << "-o" << QCoreApplication::applicationDirPath()+"/utils/tmp.png" << "-s" << "8" << "-l" << "H" << serverAddress; + attributes << "-o" << "-" /*QCoreApplication::applicationDirPath()+"/utils/tmp.png"*/ << "-s" << "8" << "-l" << "H" << serverAddress; connect(qrGenerator,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(updateImage(void))); connect(qrGenerator,SIGNAL(error(QProcess::ProcessError)),this,SLOT(openingError(QProcess::ProcessError))); qrGenerator->start(QCoreApplication::applicationDirPath()+"/utils/qrencode",attributes); @@ -71,17 +124,20 @@ void ServerConfigDialog::generateQR(const QString & serverAddress) void ServerConfigDialog::updateImage() { - //QByteArray imgBinary = qrGenerator->readAllStandardOutput(); + QByteArray imgBinary = qrGenerator->readAllStandardOutput(); //imgBinary = imgBinary.replace(0x0D0A,0x0A); - //qrCodeImage->loadFromData(imgBinary); - //if(imgBinary.isEmpty()) - // qrCode->setText("yyyyy"); - //else - // qrCode->setText("") - //delete qrGenerator; - qrCodeImage->load(QCoreApplication::applicationDirPath()+"/utils/tmp.png"); + if(!qrCodeImage->loadFromData(imgBinary)) + qrCode->setText(tr("QR generator error!")); + else + qrCode->setPixmap(*qrCodeImage); + + delete qrGenerator; + + + +/* qrCodeImage->load(QCoreApplication::applicationDirPath()+"/utils/tmp.png"); qrCode->setPixmap(*qrCodeImage); - delete qrGenerator; + delete qrGenerator;*/ } \ No newline at end of file diff --git a/YACReaderLibrary/server_config_dialog.h b/YACReaderLibrary/server_config_dialog.h index 87d3084e..a6171182 100644 --- a/YACReaderLibrary/server_config_dialog.h +++ b/YACReaderLibrary/server_config_dialog.h @@ -15,9 +15,8 @@ Q_OBJECT public: ServerConfigDialog(QWidget * parent = 0); private: - QLabel * ipLabel; - QLabel * portLabel; - QLineEdit * portEdit; + QLineEdit * ip; + QLineEdit * port; QPushButton * close; QPushButton * accept; QLabel * qrCode; diff --git a/common/comic_flow_widget.cpp b/common/comic_flow_widget.cpp index e0891027..364bad47 100644 --- a/common/comic_flow_widget.cpp +++ b/common/comic_flow_widget.cpp @@ -111,8 +111,10 @@ void ComicFlowWidgetSW::mouseDoubleClickEvent(QMouseEvent* event) { flow->mouseDoubleClickEvent(event); } - - +void ComicFlowWidgetSW::updateConfig(QSettings * settings) +{ + //flow->setFlowType(flowType); +} ////////////////////////////////////////////////////////////////////////// @@ -131,6 +133,7 @@ ComicFlowWidgetGL::ComicFlowWidgetGL(QWidget * parent) QVBoxLayout * l = new QVBoxLayout; l->addWidget(flow); + l->setContentsMargins(0,0,0,0); setLayout(l); //TODO eleminar "padding" @@ -232,4 +235,77 @@ void ComicFlowWidgetGL::resizeEvent(QResizeEvent* event) void ComicFlowWidgetGL::mouseDoubleClickEvent(QMouseEvent* event) { flow->mouseDoubleClickEvent(event); -} \ No newline at end of file +} + +void ComicFlowWidgetGL::updateConfig(QSettings * settings) +{ + Performance performance = medium; + + switch (settings->value("performance").toInt()) + { + case 0: + performance = low; + break; + case 1: + performance = medium; + break; + case 2: + performance = high; + break; + case 3: + performance = ultraHigh; + break; + } + + flow->setPerformance(performance); + + switch (settings->value("flowType").toInt()) + { + case 0: + flow->setPreset(presetYACReaderFlowClassicConfig); + return; + case 1: + flow->setPreset(presetYACReaderFlowStripeConfig); + return; + case 2: + flow->setPreset(presetYACReaderFlowOverlappedStripeConfig); + return; + case 3: + flow->setPreset(defaultYACReaderFlowConfig); + return; + case 4: + flow->setPreset(pressetYACReaderFlowDownConfig); + return; + } + + + //custom config + + flow->setCF_RX(settings->value("xRotation").toInt()); + flow->setCF_Y(settings->value("yPosition").toInt()); + flow->setX_Distance(settings->value("coverDistance").toInt()); + flow->setCenter_Distance(settings->value("centralDistance").toInt()); + flow->setCF_Z(settings->value("zoomLevel").toInt()); + flow->setY_Distance(settings->value("yCoverOffset").toInt()); + flow->setZ_Distance(settings->value("zCoverOffset").toInt()); + flow->setRotation(settings->value("coverRotation").toInt()); + flow->setFadeOutDist(settings->value("fadeOutDist").toInt()); + flow->setLightStrenght(settings->value("lightStrength").toInt()); + flow->setMaxAngle(settings->value("maxAngle").toInt()); + +/* flow->setVisibility(settings->value("visibilityDistance").toInt()); + flow->setLightStrenght(settings->value("lightStrength").toInt())*/; + +} + +//void ComicFlowWidgetGL::setCF_RX(int value){ flow->setCF_RX(value);} +//void ComicFlowWidgetGL::setCF_RY(int value){ flow->setCF_RY(value);} +//void ComicFlowWidgetGL::setCF_RZ(int value){ flow->setCF_RZ(value);} +//void ComicFlowWidgetGL::setZoom(int zoom){ flow->setZoom(zoom);} +//void ComicFlowWidgetGL::setRotation(int angle){ flow->setRotation(angle);} +//void ComicFlowWidgetGL::setX_Distance(int distance){ flow->setX_Distance(distance);} +//void ComicFlowWidgetGL::setCenter_Distance(int distance){ flow->setCenter_Distance(distance);} +//void ComicFlowWidgetGL::setZ_Distance(int distance){ flow->setZ_Distance(distance);} +//void ComicFlowWidgetGL::setCF_Y(int value){ flow->setCF_Y(value);} +//void ComicFlowWidgetGL::setY_Distance(int value){ flow->setY_Distance(value);} +//void ComicFlowWidgetGL::setPreset(const Preset & p){ flow->setPreset(p);} \ No newline at end of file diff --git a/common/comic_flow_widget.h b/common/comic_flow_widget.h index d6d53f8b..5709e675 100644 --- a/common/comic_flow_widget.h +++ b/common/comic_flow_widget.h @@ -27,6 +27,7 @@ public slots: virtual void updateMarks() = 0; virtual void setFlowType(PictureFlow::FlowType flowType) = 0; virtual void render() = 0; + virtual void updateConfig(QSettings * settings) = 0; signals: void centerIndexChanged(int); void selected(unsigned int); @@ -55,6 +56,7 @@ public: void updateMarks(); void setFlowType(PictureFlow::FlowType flowType); void render(); + void updateConfig(QSettings * settings); protected: void keyPressEvent(QKeyEvent* event); void paintEvent(QPaintEvent *event); @@ -87,6 +89,25 @@ public: void updateMarks(); void setFlowType(PictureFlow::FlowType flowType); void render(); + void updateConfig(QSettings * settings); +//public slots: +// void setCF_RX(int value); +// //the Y Rotation of the Coverflow +// void setCF_RY(int value); +// //the Z Rotation of the Coverflow +// void setCF_RZ(int value); +// //perspective +// void setZoom(int zoom); +// void setRotation(int angle); +// //sets the distance between the covers +// void setX_Distance(int distance); +// //sets the distance between the centered and the non centered covers +// void setCenter_Distance(int distance); +// //sets the pushback amount +// void setZ_Distance(int distance); +// void setCF_Y(int value); +// void setY_Distance(int value); +// void setPreset(const Preset & p); protected: void keyPressEvent(QKeyEvent* event); void paintEvent(QPaintEvent *event); diff --git a/common/custom_widgets.cpp b/common/custom_widgets.cpp index ad33b663..9a9ed301 100644 --- a/common/custom_widgets.cpp +++ b/common/custom_widgets.cpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "qnaturalsorting.h" @@ -462,4 +464,55 @@ void YACReaderFieldPlainTextEdit::setDisabled(bool disabled) if(disabled) setPlainText(tr("Click to overwrite")); QPlainTextEdit::setDisabled(disabled); -} \ No newline at end of file +} + + +YACReaderSpinSliderWidget::YACReaderSpinSliderWidget(QWidget * parent) + :QWidget(parent) +{ + QHBoxLayout * layout = new QHBoxLayout; + layout->addWidget(label = new QLabel(this)); + layout->addStretch(); + spinBox = new QSpinBox(this); + layout->addWidget(spinBox); + slider = new QSlider(Qt::Horizontal,this); + layout->addWidget(slider); + + connect(spinBox, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int))); + connect(slider, SIGNAL(valueChanged(int)), spinBox, SLOT(setValue(int))); + + connect(spinBox, SIGNAL(valueChanged(int)), this, SIGNAL(valueChanged(int))); + + setLayout(layout); +} + +void YACReaderSpinSliderWidget::setRange(int lowValue, int topValue, int step) +{ + spinBox->setMinimum(lowValue); + spinBox->setMaximum(topValue); + spinBox->setSingleStep(step); + + slider->setMinimum(lowValue); + slider->setMaximum(topValue); + slider->setSingleStep(step); +} + +void YACReaderSpinSliderWidget::setValue(int value) +{ + spinBox->setValue(value); +} + +void YACReaderSpinSliderWidget::setText(const QString & text) +{ + label->setText(text); +} + +int YACReaderSpinSliderWidget::getValue() +{ + return spinBox->value(); +} + +QSize YACReaderSpinSliderWidget::minimumSizeHint() const +{ + return QSize(220, 25); +} diff --git a/common/custom_widgets.h b/common/custom_widgets.h index d2e0d75a..12f9ded6 100644 --- a/common/custom_widgets.h +++ b/common/custom_widgets.h @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include #include "pictureflow.h" @@ -184,6 +187,26 @@ private: // } //}; +class YACReaderSpinSliderWidget : public QWidget +{ + Q_OBJECT +private: + QLabel * label; + QSpinBox * spinBox; + QSlider * slider; +public: + YACReaderSpinSliderWidget(QWidget * parent = 0); +public slots: + void setRange(int lowValue, int topValue, int step=1); + void setValue(int value); + void setText(const QString & text); + int getValue(); + QSize minimumSizeHint() const; +signals: + void valueChanged(int); + +}; + #endif diff --git a/common/pictureflow.h b/common/pictureflow.h index 8e7b6664..a1da0c6b 100644 --- a/common/pictureflow.h +++ b/common/pictureflow.h @@ -1,191 +1,194 @@ -/* - PictureFlow - animated image show widget - http://pictureflow.googlecode.com - - Copyright (C) 2008 Ariya Hidayat (ariya@kde.org) - Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#ifndef PICTUREFLOW_H -#define PICTUREFLOW_H - -#include - -class PictureFlowPrivate; - -/*! - Class PictureFlow implements an image show widget with animation effect - like Apple's CoverFlow (in iTunes and iPod). Images are arranged in form - of slides, one main slide is shown at the center with few slides on - the left and right sides of the center slide. When the next or previous - slide is brought to the front, the whole slides flow to the right or - the right with smooth animation effect; until the new slide is finally - placed at the center. - - */ -class PictureFlow : public QWidget -{ -Q_OBJECT - - Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) - Q_PROPERTY(QSize slideSize READ slideSize WRITE setSlideSize) - Q_PROPERTY(int slideCount READ slideCount) - Q_PROPERTY(int centerIndex READ centerIndex WRITE setCenterIndex) - -public: - - enum ReflectionEffect - { - NoReflection, - PlainReflection, - BlurredReflection - }; - - enum FlowType - { - CoverFlowLike, - Strip, - StripOverlapped - }; - - /*! - Creates a new PictureFlow widget. - */ - PictureFlow(QWidget* parent = 0, FlowType flowType = CoverFlowLike); - - /*! - Destroys the widget. - */ - ~PictureFlow(); - - /*! - Returns the background color. - */ - QColor backgroundColor() const; - - /*! - Sets the background color. By default it is black. - */ - void setBackgroundColor(const QColor& c); - - /*! - Returns the dimension of each slide (in pixels). - */ - QSize slideSize() const; - - /*! - Sets the dimension of each slide (in pixels). - */ - void setSlideSize(QSize size); - - /*! - Returns the total number of slides. - */ - int slideCount() const; - - /*! - Returns QImage of specified slide. - */ - QImage slide(int index) const; - - /*! - Returns the index of slide currently shown in the middle of the viewport. - */ - int centerIndex() const; - - /*! - Returns the effect applied to the reflection. - */ - ReflectionEffect reflectionEffect() const; - - /*! - Sets the effect applied to the reflection. The default is PlainReflection. - */ - void setReflectionEffect(ReflectionEffect effect); - - -public slots: - - /*! - Adds a new slide. - */ - void addSlide(const QImage& image); - - /*! - Adds a new slide. - */ - void addSlide(const QPixmap& pixmap); - - /*! - Sets an image for specified slide. If the slide already exists, - it will be replaced. - */ - void setSlide(int index, const QImage& image); - - /*! - Sets a pixmap for specified slide. If the slide already exists, - it will be replaced. - */ - void setSlide(int index, const QPixmap& pixmap); - - /*! - Sets slide to be shown in the middle of the viewport. No animation - effect will be produced, unlike using showSlide. - */ - void setCenterIndex(int index); - - /*! - Clears all slides. - */ - void clear(); - - /*! - Shows previous slide using animation effect. - */ - void showPrevious(); - - /*! - Shows next slide using animation effect. - */ - void showNext(); - - /*! - Go to specified slide using animation effect. - */ - void showSlide(int index); - - /*! - Rerender the widget. Normally this function will be automatically invoked - whenever necessary, e.g. during the transition animation. - */ - void render(); - - /*! - Schedules a rendering update. Unlike render(), this function does not cause - immediate rendering. - */ - void triggerRender(); - - void setFlowType(FlowType flowType); - +/* + PictureFlow - animated image show widget + http://pictureflow.googlecode.com + + Copyright (C) 2008 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef PICTUREFLOW_H +#define PICTUREFLOW_H + +#include + +class PictureFlowPrivate; + +/*! + Class PictureFlow implements an image show widget with animation effect + like Apple's CoverFlow (in iTunes and iPod). Images are arranged in form + of slides, one main slide is shown at the center with few slides on + the left and right sides of the center slide. When the next or previous + slide is brought to the front, the whole slides flow to the right or + the right with smooth animation effect; until the new slide is finally + placed at the center. + + */ +class PictureFlow : public QWidget +{ +Q_OBJECT + + Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) + Q_PROPERTY(QSize slideSize READ slideSize WRITE setSlideSize) + Q_PROPERTY(int slideCount READ slideCount) + Q_PROPERTY(int centerIndex READ centerIndex WRITE setCenterIndex) + +public: + + enum ReflectionEffect + { + NoReflection, + PlainReflection, + BlurredReflection + }; + + enum FlowType + { + CoverFlowLike, + Strip, + StripOverlapped, + Modern, + Roulette, + Custom + }; + + /*! + Creates a new PictureFlow widget. + */ + PictureFlow(QWidget* parent = 0, FlowType flowType = CoverFlowLike); + + /*! + Destroys the widget. + */ + ~PictureFlow(); + + /*! + Returns the background color. + */ + QColor backgroundColor() const; + + /*! + Sets the background color. By default it is black. + */ + void setBackgroundColor(const QColor& c); + + /*! + Returns the dimension of each slide (in pixels). + */ + QSize slideSize() const; + + /*! + Sets the dimension of each slide (in pixels). + */ + void setSlideSize(QSize size); + + /*! + Returns the total number of slides. + */ + int slideCount() const; + + /*! + Returns QImage of specified slide. + */ + QImage slide(int index) const; + + /*! + Returns the index of slide currently shown in the middle of the viewport. + */ + int centerIndex() const; + + /*! + Returns the effect applied to the reflection. + */ + ReflectionEffect reflectionEffect() const; + + /*! + Sets the effect applied to the reflection. The default is PlainReflection. + */ + void setReflectionEffect(ReflectionEffect effect); + + +public slots: + + /*! + Adds a new slide. + */ + void addSlide(const QImage& image); + + /*! + Adds a new slide. + */ + void addSlide(const QPixmap& pixmap); + + /*! + Sets an image for specified slide. If the slide already exists, + it will be replaced. + */ + void setSlide(int index, const QImage& image); + + /*! + Sets a pixmap for specified slide. If the slide already exists, + it will be replaced. + */ + void setSlide(int index, const QPixmap& pixmap); + + /*! + Sets slide to be shown in the middle of the viewport. No animation + effect will be produced, unlike using showSlide. + */ + void setCenterIndex(int index); + + /*! + Clears all slides. + */ + void clear(); + + /*! + Shows previous slide using animation effect. + */ + void showPrevious(); + + /*! + Shows next slide using animation effect. + */ + void showNext(); + + /*! + Go to specified slide using animation effect. + */ + void showSlide(int index); + + /*! + Rerender the widget. Normally this function will be automatically invoked + whenever necessary, e.g. during the transition animation. + */ + void render(); + + /*! + Schedules a rendering update. Unlike render(), this function does not cause + immediate rendering. + */ + void triggerRender(); + + void setFlowType(FlowType flowType); + void setMarkImage(const QImage & mark); void markSlide(int index); @@ -200,25 +203,25 @@ public slots: QVector getMarks(); - -signals: - void centerIndexChanged(int index); - -public: - void paintEvent(QPaintEvent *event); - void keyPressEvent(QKeyEvent* event); - void mousePressEvent(QMouseEvent* event); - void resizeEvent(QResizeEvent* event); - -private slots: - void updateAnimation(); - -private: - PictureFlowPrivate* d; - QImage mark; - QVector marks; - int framesSkip; -}; - -#endif // PICTUREFLOW_H - + +signals: + void centerIndexChanged(int index); + +public: + void paintEvent(QPaintEvent *event); + void keyPressEvent(QKeyEvent* event); + void mousePressEvent(QMouseEvent* event); + void resizeEvent(QResizeEvent* event); + +private slots: + void updateAnimation(); + +private: + PictureFlowPrivate* d; + QImage mark; + QVector marks; + int framesSkip; +}; + +#endif // PICTUREFLOW_H + diff --git a/common/yacreader_flow_gl.cpp b/common/yacreader_flow_gl.cpp index a9d2feb9..0abe288a 100644 --- a/common/yacreader_flow_gl.cpp +++ b/common/yacreader_flow_gl.cpp @@ -25,7 +25,7 @@ struct Preset defaultYACReaderFlowConfig = { 0, //CF_X the X Position of the Coverflow 0, //CF_Y the Y Position of the Coverflow - -10, //CF_Z the Z Position of the Coverflow + -12, //CF_Z the Z Position of the Coverflow 15, //CF_RX the X Rotation of the Coverflow 0, //CF_RY the Y Rotation of the Coverflow @@ -35,7 +35,9 @@ struct Preset defaultYACReaderFlowConfig = { 0.18, //X_Distance sets the distance between the covers 1, //Center_Distance sets the distance between the centered and the non centered covers 0.1, //Z_Distance sets the pushback amount - 0.0 //Y_Distance sets the elevation amount + 0.0, //Y_Distance sets the elevation amount + + 30 //zoom level }; @@ -63,7 +65,9 @@ struct Preset presetYACReaderFlowClassicConfig = { 0.18, //X_Distance sets the distance between the covers 1, //Center_Distance sets the distance between the centered and the non centered covers 0.1, //Z_Distance sets the pushback amount - 0.0 //Y_Distance sets the elevation amount + 0.0, //Y_Distance sets the elevation amount + + 22 //zoom level }; @@ -91,7 +95,9 @@ struct Preset presetYACReaderFlowStripeConfig = { 1.1, //X_Distance sets the distance between the covers 0.2, //Center_Distance sets the distance between the centered and the non centered covers 0.01, //Z_Distance sets the pushback amount - 0.0 //Y_Distance sets the elevation amount + 0.0, //Y_Distance sets the elevation amount + + 22 //zoom level }; @@ -119,7 +125,9 @@ struct Preset presetYACReaderFlowOverlappedStripeConfig = { 0.18, //X_Distance sets the distance between the covers 1, //Center_Distance sets the distance between the centered and the non centered covers 0.1, //Z_Distance sets the pushback amount - 0.0 //Y_Distance sets the elevation amount + 0.0, //Y_Distance sets the elevation amount + + 22 //zoom level }; @@ -133,7 +141,7 @@ struct Preset pressetYACReaderFlowUpConfig = { 3, //View_rotate_light_strenght sets the light strenght on rotation 0.08, //View_rotate_add sets the speed of the rotation 0.08, //View_rotate_sub sets the speed of reversing the rotation - 30, //View_angle sets the maximum view angle + 5, //View_angle sets the maximum view angle 0, //CF_X the X Position of the Coverflow -0.2, //CF_Y the Y Position of the Coverflow @@ -147,7 +155,9 @@ struct Preset pressetYACReaderFlowUpConfig = { 0.18, //X_Distance sets the distance between the covers 1, //Center_Distance sets the distance between the centered and the non centered covers 0.1, //Z_Distance sets the pushback amount - -0.1 //Y_Distance sets the elevation amount + -0.1, //Y_Distance sets the elevation amount + + 22 //zoom level }; @@ -161,7 +171,7 @@ struct Preset pressetYACReaderFlowDownConfig = { 3, //View_rotate_light_strenght sets the light strenght on rotation 0.08, //View_rotate_add sets the speed of the rotation 0.08, //View_rotate_sub sets the speed of reversing the rotation - 30, //View_angle sets the maximum view angle + 5, //View_angle sets the maximum view angle 0, //CF_X the X Position of the Coverflow -0.2, //CF_Y the Y Position of the Coverflow @@ -175,11 +185,13 @@ struct Preset pressetYACReaderFlowDownConfig = { 0.18, //X_Distance sets the distance between the covers 1, //Center_Distance sets the distance between the centered and the non centered covers 0.1, //Z_Distance sets the pushback amount - 0.1 //Y_Distance sets the elevation amount + 0.1, //Y_Distance sets the elevation amount + + 22 //zoom level }; /*Constructor*/ YACReaderFlowGL::YACReaderFlowGL(QWidget *parent,struct Preset p) - :QGLWidget(QGLFormat(QGL::DoubleBuffer), parent),numObjects(0),lazyPopulateObjects(-1) + :QGLWidget(QGLFormat(QGL::SampleBuffers), parent),numObjects(0),lazyPopulateObjects(-1) { updateCount = 0; config = p; @@ -217,7 +229,6 @@ YACReaderFlowGL::YACReaderFlowGL(QWidget *parent,struct Preset p) loaderThread->start();*/ timerId = startTimer(16); - } void YACReaderFlowGL::timerEvent(QTimerEvent * event) @@ -257,19 +268,9 @@ void YACReaderFlowGL::initializeGL() defaultTexture = bindTexture(QImage(":/images/defaultCover.png"),GL_TEXTURE_2D,GL_RGBA,QGLContext::LinearFilteringBindOption | QGLContext::MipmapBindOption); markTexture = bindTexture(QImage(":/images/setRead.png"),GL_TEXTURE_2D,GL_RGBA,QGLContext::LinearFilteringBindOption | QGLContext::MipmapBindOption); + if(lazyPopulateObjects!=-1) populate(lazyPopulateObjects); //TODO esto es responsabilidad del usuario de la clase - - //float x = 0.5; - //int i; - //int n = 20; - //for(i = 0;iInsert("cover", cover, x, y); - //} - } void YACReaderFlowGL::paintGL() @@ -600,7 +601,8 @@ void YACReaderFlowGL::updatePositions() if(!viewRotateActive){ viewRotate += (0-viewRotate)*config.viewRotateSub; } - if(viewRotate < 0.2) + + if(abs (cfImages[currentSelected].current.x - cfImages[currentSelected].animEnd.x) < 1)//viewRotate < 0.2) { if(updateCount >= 0) //TODO parametrizar { @@ -677,6 +679,7 @@ void YACReaderFlowGL::replace(char *name, GLuint Tex, float x, float y,int item) void YACReaderFlowGL::populate(int n) { + emit centerIndexChanged(0); float x = 1; float y = 1 * (700/480.0); int i; @@ -696,13 +699,14 @@ void YACReaderFlowGL::populate(int n) loaded = QVector(n,false); marks = QVector(n,false); - emit centerIndexChanged(0); + //worker->start(); } void YACReaderFlowGL::reset() { + currentSelected = 0; loaded.clear(); for(int i = 0;i0) delete[] cfImages; numObjects = 0; - currentSelected = 0; + } //slots @@ -771,16 +775,54 @@ void YACReaderFlowGL::setCF_Y(int value) config.cfY = value/100.0; } +void YACReaderFlowGL::setCF_Z(int value) +{ + config.cfZ = value; +} + void YACReaderFlowGL::setY_Distance(int value) { config.yDistance = value / 100.0; } +void YACReaderFlowGL::setFadeOutDist(int value) +{ + config.animationFadeOutDist = value; +} + +void YACReaderFlowGL::setLightStrenght(int value) +{ + config.viewRotateLightStrenght = value; +} + +void YACReaderFlowGL::setMaxAngle(int value) +{ + config.viewAngle = value; +} + void YACReaderFlowGL::setPreset(const Preset & p) { config = p; } +void YACReaderFlowGL::setPerformance(Performance performance) +{ + this->performance = performance; + + //if(performance = ultraHigh) + //{ + // QGLFormat f = format(); + // f.setSwapInterval(1); + // setFormat(f); + //} + //else + //{ + // QGLFormat f = format(); + // f.setSwapInterval(0); + // setFormat(f); + //} +} + void YACReaderFlowGL::setShowMarks(bool value) { showMarks = value; @@ -954,7 +996,11 @@ void YACReaderComicFlowGL::updateImageData() { float x = 1; QImage img = worker->result(); - GLuint cover = bindTexture(img, GL_TEXTURE_2D,GL_RGB,QGLContext::LinearFilteringBindOption); + GLuint cover; + if(performance == high || performance == ultraHigh) + cover = bindTexture(img, GL_TEXTURE_2D,GL_RGB,QGLContext::LinearFilteringBindOption | QGLContext::MipmapBindOption); + else + cover = bindTexture(img, GL_TEXTURE_2D,GL_RGB,QGLContext::LinearFilteringBindOption); float y = 1 * (float(img.height())/img.width()); replace("cover", cover, x, y,idx); /*CFImages[idx].width = x; @@ -968,16 +1014,31 @@ void YACReaderComicFlowGL::updateImageData() // try to load only few images on the left and right side // i.e. all visible ones plus some extra -#define COUNT 8 - int indexes[2*COUNT+1]; + int count=8; + switch(performance) + { + case low: + count = 8; + break; + case medium: + count = 10; + break; + case high: + count = 12; + break; + case ultraHigh: + count = 14; + break; + } + int * indexes = new int[2*count+1]; int center = currentSelected; indexes[0] = center; - for(int j = 0; j < COUNT; j++) + for(int j = 0; j < count; j++) { indexes[j*2+1] = center+j+1; indexes[j*2+2] = center-j-1; } - for(int c = 0; c < 2*COUNT+1; c++) + for(int c = 0; c < 2*count+1; c++) { int i = indexes[c]; if((i >= 0) && (i < numObjects)) @@ -993,6 +1054,7 @@ void YACReaderComicFlowGL::updateImageData() worker->generate(i, fname); } + delete[] indexes; return; } } @@ -1026,7 +1088,11 @@ void YACReaderPageFlowGL::updateImageData() { float x = 1; QImage img = worker->result(); - GLuint cover = bindTexture(img, GL_TEXTURE_2D,GL_RGB,QGLContext::LinearFilteringBindOption | QGLContext::MipmapBindOption); + GLuint cover; + if(performance == high || performance == ultraHigh) + cover = bindTexture(img, GL_TEXTURE_2D,GL_RGB,QGLContext::LinearFilteringBindOption | QGLContext::MipmapBindOption); + else + cover = bindTexture(img, GL_TEXTURE_2D,GL_RGB,QGLContext::LinearFilteringBindOption); float y = 1 * (float(img.height())/img.width()); replace("cover", cover, x, y,idx); /*CFImages[idx].width = x; @@ -1040,16 +1106,31 @@ void YACReaderPageFlowGL::updateImageData() // try to load only few images on the left and right side // i.e. all visible ones plus some extra -#define COUNT 8 - int indexes[2*COUNT+1]; + int count=8; + switch(performance) + { + case low: + count = 8; + break; + case medium: + count = 10; + break; + case high: + count = 12; + break; + case ultraHigh: + count = 14; + break; + } + int * indexes = new int[2*count+1]; int center = currentSelected; indexes[0] = center; - for(int j = 0; j < COUNT; j++) + for(int j = 0; j < count; j++) { indexes[j*2+1] = center+j+1; indexes[j*2+2] = center-j-1; } - for(int c = 0; c < 2*COUNT+1; c++) + for(int c = 0; c < 2*count+1; c++) { int i = indexes[c]; if((i >= 0) && (i < numObjects)) @@ -1065,6 +1146,7 @@ void YACReaderPageFlowGL::updateImageData() worker->generate(i, rawImages.at(i)); + delete[] indexes; return; } } @@ -1096,8 +1178,19 @@ QImage ImageLoaderGL::loadImage(const QString& fileName) //resultTexture = flow->bindTexture(image,GL_TEXTURE_2D); //TODO parametrizar - image = image.scaledToWidth(256,Qt::SmoothTransformation); - + switch(flow->performance) + { + case low: + image = image.scaledToWidth(200,Qt::SmoothTransformation); + break; + case medium: + image = image.scaledToWidth(256,Qt::SmoothTransformation); + break; + case high: + image = image.scaledToWidth(320,Qt::SmoothTransformation); + break; + + } if(!result) return QImage(); diff --git a/common/yacreader_flow_gl.h b/common/yacreader_flow_gl.h index 7fa5dd3a..e00cccf9 100644 --- a/common/yacreader_flow_gl.h +++ b/common/yacreader_flow_gl.h @@ -18,6 +18,14 @@ class QGLContext; class WidgetLoader; class ImageLoaderByteArrayGL; +typedef enum Performance +{ + low=0, + medium, + high, + ultraHigh +}; + //Cover Vector typedef struct RVect{ float x; @@ -84,6 +92,8 @@ struct Preset{ float zDistance; //sets the elevation amount float yDistance; + + float zoom; }; extern struct Preset defaultYACReaderFlowConfig; @@ -118,9 +128,7 @@ protected: void initializeGL(); void paintGL(); void timerEvent(QTimerEvent *); - -public: //number of Covers int numObjects; int lazyPopulateObjects; @@ -130,6 +138,8 @@ public: QList paths; CFImage * cfImages; + Performance performance; + /*** Animation Settings ***/ Preset config; @@ -150,9 +160,12 @@ public: /*** System info ***/ float viewRotate; + +public: + /*Constructor*/ - YACReaderFlowGL(QWidget *parent = 0,struct Preset p = defaultYACReaderFlowConfig); + YACReaderFlowGL(QWidget *parent = 0,struct Preset p = pressetYACReaderFlowDownConfig); ~YACReaderFlowGL(); //size; @@ -207,11 +220,22 @@ public: void setZ_Distance(int distance); void setCF_Y(int value); + void setCF_Z(int value); void setY_Distance(int value); + void setFadeOutDist(int value); + + void setLightStrenght(int value); + + void setMaxAngle(int value); + void setPreset(const Preset & p); + void setPerformance(Performance performance); + + + virtual void updateImageData() = 0; void reset(); @@ -237,6 +261,8 @@ public: void wheelEvent(QWheelEvent * event); void keyPressEvent(QKeyEvent *event); void resizeGL(int width, int height); + friend class ImageLoaderGL; + friend class ImageLoaderByteArrayGL; signals: void centerIndexChanged(int); @@ -260,7 +286,7 @@ public: YACReaderComicFlowGL(QWidget *parent = 0,struct Preset p = defaultYACReaderFlowConfig); void setImagePaths(QStringList paths); void updateImageData(); - + friend class ImageLoaderGL; private: ImageLoaderGL * worker; @@ -275,6 +301,7 @@ public: QVector imagesReady; QVector rawImages; QVector imagesSetted; + friend class ImageLoaderByteArrayGL; private: ImageLoaderByteArrayGL * worker; }; diff --git a/images/iphoneConfig.png b/images/iphoneConfig.png new file mode 100644 index 0000000000000000000000000000000000000000..c5b4797c41a7bdda2a16fce4bc3ca21dad33d1d6 GIT binary patch literal 16872 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 literal 0 HcmV?d00001 diff --git a/images/qrMessage.png b/images/qrMessage.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3a4f13bda0e2ea4e14fe1fac03eac2202160a5 GIT binary patch literal 2781 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 literal 0 HcmV?d00001