diff --git a/saori.pro b/saori.pro index b0f39d0..64e08b5 100644 --- a/saori.pro +++ b/saori.pro @@ -30,7 +30,8 @@ saoriaddaccountdialog.cpp \ saoriaccount.cpp \ saoriapplication.cpp \ - saoricache.cpp + saoricache.cpp \ + saoriviewentry.cpp HEADERS += \ saoriwindow.h \ @@ -40,7 +41,8 @@ saoridef.h \ saoriaccount.h \ saoriapplication.h \ - saoricache.h + saoricache.h \ + saoriviewentry.h FORMS += \ saoriwindow.ui \ diff --git a/saori.qrc b/saori.qrc index 67759cc..e708696 100644 --- a/saori.qrc +++ b/saori.qrc @@ -32,4 +32,7 @@ <file>ionicons/load-a.svg</file> <file>ionicons/alert-circled.svg</file> </qresource> + <qresource prefix="/css"> + <file>saoristyle.css</file> + </qresource> </RCC> diff --git a/saori_ja.qm b/saori_ja.qm new file mode 100644 index 0000000..4ac4e00 --- /dev/null +++ b/saori_ja.qm Binary files differ diff --git a/saori_ja.ts b/saori_ja.ts index 7e676a5..352e142 100644 --- a/saori_ja.ts +++ b/saori_ja.ts @@ -6,47 +6,47 @@ <message> <location filename="saoriaddaccountdialog.ui" line="14"/> <source>Add new Account</source> - <translation type="unfinished"></translation> + <translation>アカウントを追加</translation> </message> <message> <location filename="saoriaddaccountdialog.ui" line="22"/> <source>Instance URL:</source> - <translation type="unfinished"></translation> + <translation>インスタンスURL:</translation> </message> <message> <location filename="saoriaddaccountdialog.ui" line="29"/> <source>Get Access token:</source> - <translation type="unfinished"></translation> + <translation>アクセストークンを取得:</translation> </message> <message> <location filename="saoriaddaccountdialog.ui" line="36"/> <source>Open Authorize page:</source> - <translation type="unfinished"></translation> + <translation>認証ページを開く:</translation> </message> <message> <location filename="saoriaddaccountdialog.ui" line="71"/> <source>Check</source> - <translation type="unfinished"></translation> + <translation>確認</translation> </message> <message> <location filename="saoriaddaccountdialog.ui" line="88"/> <source>Get!</source> - <translation type="unfinished"></translation> + <translation>取得!</translation> </message> <message> <location filename="saoriaddaccountdialog.ui" line="43"/> <source>Authorize Code:</source> - <translation type="unfinished"></translation> + <translation>認証コード:</translation> </message> <message> <location filename="saoriaddaccountdialog.ui" line="78"/> <source>Open WebBrowser</source> - <translation type="unfinished"></translation> + <translation>ブラウザを開く</translation> </message> <message> <location filename="saoriaddaccountdialog.ui" line="57"/> <source>Account:</source> - <translation type="unfinished"></translation> + <translation>アカウント:</translation> </message> </context> <context> @@ -57,145 +57,166 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="saoriview.ui" line="43"/> + <location filename="saoriview.ui" line="67"/> <source>Pin</source> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriview.ui" line="57"/> + <location filename="saoriview.ui" line="81"/> <source>Notify</source> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriview.ui" line="71"/> + <location filename="saoriview.ui" line="95"/> <source>Auto reload</source> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriview.ui" line="85"/> + <location filename="saoriview.ui" line="109"/> <source>Newest</source> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriview.ui" line="96"/> + <location filename="saoriview.ui" line="120"/> <source>Reload</source> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriview.ui" line="126"/> + <location filename="saoriview.ui" line="150"/> <source>Toot</source> <translation type="unfinished"></translation> </message> + <message> + <location filename="saoriview.cpp" line="97"/> + <source> following: </source> + <translation> フォロー: </translation> + </message> + <message> + <location filename="saoriview.cpp" line="99"/> + <source> followers: </source> + <translation type="unfinished"> フォロワー: </translation> + </message> </context> <context> <name>SaoriWindow</name> <message> <location filename="saoriwindow.ui" line="14"/> - <source>SaoriWindow</source> + <source>Saori</source> + <oldsource>SaoriWindow</oldsource> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriwindow.ui" line="59"/> + <location filename="saoriwindow.ui" line="63"/> <source>&File</source> <translation type="unfinished">ファイル(&F)</translation> </message> <message> - <location filename="saoriwindow.ui" line="65"/> + <location filename="saoriwindow.ui" line="69"/> <source>&View</source> <translation type="unfinished">表示(&V)</translation> </message> <message> - <location filename="saoriwindow.ui" line="71"/> + <location filename="saoriwindow.ui" line="77"/> <source>&Edit</source> <translation type="unfinished">編集(&E)</translation> </message> <message> - <location filename="saoriwindow.ui" line="79"/> + <location filename="saoriwindow.ui" line="85"/> <source>&Help</source> <translation type="unfinished">ヘルプ(&H)</translation> </message> <message> - <location filename="saoriwindow.ui" line="113"/> + <location filename="saoriwindow.ui" line="119"/> <source>Timeline &list</source> <translation type="unfinished">タイムラインリスト(&l)</translation> </message> <message> - <location filename="saoriwindow.ui" line="132"/> + <location filename="saoriwindow.ui" line="138"/> <source>Auto reload Interval (minutes)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriwindow.ui" line="146"/> + <location filename="saoriwindow.ui" line="152"/> <source>List</source> <translation type="unfinished">リスト</translation> </message> <message> - <location filename="saoriwindow.ui" line="151"/> + <location filename="saoriwindow.ui" line="157"/> <source>Account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriwindow.ui" line="156"/> + <location filename="saoriwindow.ui" line="162"/> <source>Instance</source> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriwindow.ui" line="166"/> + <location filename="saoriwindow.ui" line="172"/> <source>&Quit</source> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriwindow.ui" line="177"/> + <location filename="saoriwindow.ui" line="183"/> <source>Show/Hide Timeline &List</source> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriwindow.ui" line="182"/> + <location filename="saoriwindow.ui" line="188"/> <source>&Open Timeline</source> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriwindow.ui" line="187"/> + <location filename="saoriwindow.ui" line="193"/> <source>&Add Account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriwindow.ui" line="192"/> + <location filename="saoriwindow.ui" line="198"/> <source>&Remove Account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="saoriwindow.cpp" line="59"/> + <location filename="saoriwindow.ui" line="206"/> + <source>&TabbedView mode</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="saoriwindow.ui" line="211"/> + <source>T&iled</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="saoriwindow.cpp" line="109"/> <source>Accounts</source> <translation type="unfinished">アカウント</translation> </message> <message> - <location filename="saoriwindow.cpp" line="72"/> + <location filename="saoriwindow.cpp" line="56"/> <source>home</source> <translation type="unfinished">ホーム</translation> </message> <message> - <location filename="saoriwindow.cpp" line="76"/> + <location filename="saoriwindow.cpp" line="60"/> <source>local</source> <translation type="unfinished">ローカルタイムライン</translation> </message> <message> - <location filename="saoriwindow.cpp" line="80"/> + <location filename="saoriwindow.cpp" line="64"/> <source>public</source> <translation type="unfinished">連合タイムライン</translation> </message> <message> - <location filename="saoriwindow.cpp" line="84"/> + <location filename="saoriwindow.cpp" line="68"/> <source>notifications</source> <translation type="unfinished">通知</translation> </message> <message> - <location filename="saoriwindow.cpp" line="98"/> + <location filename="saoriwindow.cpp" line="124"/> <source>Instances</source> <translation type="unfinished">インスタンス</translation> </message> <message> - <location filename="saoriwindow.cpp" line="102"/> + <location filename="saoriwindow.cpp" line="128"/> <source>Infomation</source> <translation type="unfinished">情報</translation> </message> diff --git a/saoristyle.css b/saoristyle.css new file mode 100644 index 0000000..558840f --- /dev/null +++ b/saoristyle.css @@ -0,0 +1,16 @@ +div.user_info { + font-size:small; +} + +span.display_name { + font-size:large; +} + +span.acct { + color:gray; +} + +img.avatar { + float:left; +} + diff --git a/saoriview.cpp b/saoriview.cpp index a730d49..2883196 100644 --- a/saoriview.cpp +++ b/saoriview.cpp @@ -28,6 +28,9 @@ #include "ui_saoriview.h" #include <QJsonDocument> #include <QJsonObject> +#include <QJsonArray> +#include <QLabel> +#include <QScrollBar> #include <QDebug> #include <saoriapplication.h> @@ -63,9 +66,51 @@ return nullptr; } +const QString SaoriView::statusParser(const QJsonObject json) +{ + QString result; + if (json.isEmpty()) return QString(); + result += "<div class=\"status\">"; + result += accountParser(json["account"].toObject()); + result += "<div class=\"content\">"; + result += json["content"].toString(); + result += "</div>"; + result += "</div>"; + return result; +} + +const QString SaoriView::accountParser(const QJsonObject json) +{ + QString result; + if (json.isEmpty()) return QString(); + result += "<div class=\"account\">"; + result += "<span class=\"avatar\"><hr />"; + result += "<img("; + result += json["avatar"].toString(); + result += ") width=64 heght=64 style=\"float:left;\" /></span>"; + result += "<span class=\"display_name\">"; + result += json["display_name"].toString(); + result += "</span><span class=\"acct\">"; + result += json["acct"].toString(); + result += "</span>"; + result += "<div class=\"user_info\">"; + result += tr(" following: "); + result += QString::number(json["following_count"].toInt()); + result += tr(" followers: "); + result += QString::number(json["followers_count"].toInt()); + result += "</div>"; + result += "</div>"; + return result; +} + void SaoriView::recived(const QString timeline,const QByteArray data) { - if (timeline == m_viewname) { - qDebug() << QString::fromUtf8(data); + if (timeline != m_viewname) return; + QJsonArray json = QJsonDocument::fromJson(data).array(); + for (auto j:json) { + auto entry = new SaoriViewEntry(ui->scrollAreaWidgetContents); + entry->setObjectName(j.toObject()["id"].toString()); + entry->setContent(statusParser(j.toObject())); + ui->scrollAreaWidgetContents->layout()->addWidget(entry); } } diff --git a/saoriview.h b/saoriview.h index d15652d..d7c5887 100644 --- a/saoriview.h +++ b/saoriview.h @@ -28,7 +28,10 @@ #define SAORIVIEW_H #include <QWidget> -#include <QMap> +#include <QList> +#include <QJsonObject> +#include <QJsonArray> +#include <saoriviewentry.h> namespace Ui { class SaoriView; @@ -46,9 +49,15 @@ protected: QString m_viewname; QString m_account; + QList<SaoriViewEntry*> m_entries; static QList<SaoriView*> m_viewList; +protected: + const QString statusParser(const QJsonObject json); + const QString accountParser(const QJsonObject json); + const QString notificationParser(const QJsonObject json); + public slots: void recived(const QString timeline,const QByteArray data); diff --git a/saoriview.ui b/saoriview.ui index e4b2987..96650f4 100644 --- a/saoriview.ui +++ b/saoriview.ui @@ -16,18 +16,42 @@ <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QScrollArea" name="scrollArea"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOn</enum> + </property> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAsNeeded</enum> + </property> + <property name="sizeAdjustPolicy"> + <enum>QAbstractScrollArea::AdjustIgnored</enum> + </property> <property name="widgetResizable"> <bool>true</bool> </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> <widget class="QWidget" name="scrollAreaWidgetContents"> <property name="geometry"> <rect> <x>0</x> <y>0</y> - <width>307</width> + <width>293</width> <height>316</height> </rect> </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <layout class="QVBoxLayout" name="verticalLayout_2"/> </widget> </widget> diff --git a/saoriviewentry.cpp b/saoriviewentry.cpp new file mode 100644 index 0000000..e00c47f --- /dev/null +++ b/saoriviewentry.cpp @@ -0,0 +1,85 @@ +/*** + +The MIT License + +Copyright (c) 2018 Teppei Tamra (TAM) + +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. + +***/ + +#include "saoriviewentry.h" +#include "saoriapplication.h" +#include "saoricache.h" +#include <QRegExp> +#include <QDebug> + +SaoriViewEntry::SaoriViewEntry(QWidget *parent) : + QLabel(parent) +{ + setWordWrap(true); + setSizePolicy(QSizePolicy::Expanding,QSizePolicy::MinimumExpanding); + setTextFormat(Qt::RichText); + connect(SaoriApplication::saori()->cache(),&SaoriCache::downloaded,this,&SaoriViewEntry::downloaded); +} + +void SaoriViewEntry::setContent(const QString content) +{ + m_original = content; + QStringList imgs; + for(int i = 0;(i = m_original.indexOf("<img(",i)) != -1;++ i) { + int p = m_original.indexOf(')',i); + imgs << m_original.mid(i + 5,p - (i + 5)); + } + for (auto i:imgs) { + m_urlmap[i] = SaoriApplication::saori()->cache()->fileCache(QUrl(i)); + } + setText(designedText()); + qDebug() << text(); +} + +const QString SaoriViewEntry::designedText() +{ + QString result; + result += "<head><link rel=\"stylesheet\" type=\"text/css\" href=\":/css/saoristyle.css\" /></head>"; + result += "<body>"; + result += imageReplacer(); + result += "</body>"; + return result; +} + +const QString SaoriViewEntry::imageReplacer() +{ + QString result = m_original; + for (auto k:m_urlmap.keys()) { + result.replace(QString("<img(" + k + ")"), + QString("<img src=\"" + m_urlmap[k] + "\"")); + } + return result; +} + +void SaoriViewEntry::downloaded(const QUrl url) +{ + m_urlmap[url.toString()] = SaoriApplication::saori()->cache()->fileCache(url); + setText(designedText()); + for (auto i:m_urlmap) { + if (i.indexOf(":/")) return; + } + disconnect(SaoriApplication::saori()->cache(),&SaoriCache::downloaded,this,&SaoriViewEntry::downloaded); +} diff --git a/saoriviewentry.h b/saoriviewentry.h new file mode 100644 index 0000000..4d6c718 --- /dev/null +++ b/saoriviewentry.h @@ -0,0 +1,55 @@ +/*** + +The MIT License + +Copyright (c) 2018 Teppei Tamra (TAM) + +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 SAORIVIEWENTRY_H +#define SAORIVIEWENTRY_H + +#include <QLabel> +#include <QUrl> +#include <QMap> + +class SaoriViewEntry : public QLabel +{ + Q_OBJECT + +public: + SaoriViewEntry(QWidget *parent = nullptr); + void setContent(const QString content); + +protected: + const QString designedText(); + const QString imageReplacer(); + +protected: + QString m_original; + QMap<QString,QString> m_urlmap; + +protected slots: + void downloaded(const QUrl url); + +}; + +#endif // SAORIVIEWENTRY_H