File indexing completed on 2024-04-28 15:40:17
0001 // SPDX-FileCopyrightText: 2019-2022 The KPhotoAlbum Development Team 0002 // 0003 // SPDX-License-Identifier: GPL-2.0-or-later 0004 0005 #include "RemoteInterface.h" 0006 0007 #include "RemoteCommand.h" 0008 #include "RemoteImageRequest.h" 0009 #include "Server.h" 0010 #include "Types.h" 0011 0012 #include <Browser/FlatCategoryModel.h> 0013 #include <DB/Category.h> 0014 #include <DB/CategoryCollection.h> 0015 #include <DB/CategoryPtr.h> 0016 #include <DB/ImageDB.h> 0017 #include <DB/ImageInfo.h> 0018 #include <DB/ImageInfoPtr.h> 0019 #include <DB/ImageSearchInfo.h> 0020 #include <ImageManager/AsyncLoader.h> 0021 #include <MainWindow/DirtyIndicator.h> 0022 #include <Utilities/DescriptionUtil.h> 0023 0024 #include <KLocalizedString> 0025 #include <QBuffer> 0026 #include <QDataStream> 0027 #include <QImage> 0028 #include <QPainter> 0029 #include <QTcpSocket> 0030 #include <algorithm> 0031 #include <kiconloader.h> 0032 #include <tuple> 0033 0034 using namespace RemoteControl; 0035 0036 RemoteInterface &RemoteInterface::instance() 0037 { 0038 static RemoteInterface instance; 0039 return instance; 0040 } 0041 0042 RemoteInterface::RemoteInterface(QObject *parent) 0043 : QObject(parent) 0044 , m_connection(new Server(this)) 0045 { 0046 connect(m_connection, &Server::gotCommand, this, &RemoteInterface::handleCommand); 0047 connect(m_connection, &Server::connected, this, &RemoteInterface::connected); 0048 connect(m_connection, &Server::disConnected, this, &RemoteInterface::disConnected); 0049 connect(m_connection, &Server::stoppedListening, this, &RemoteInterface::stoppedListening); 0050 } 0051 0052 DB::ImageSearchInfo RemoteInterface::convert(const SearchInfo &searchInfo) const 0053 { 0054 DB::ImageSearchInfo dbSearchInfo; 0055 QString category; 0056 QString value; 0057 for (auto item : searchInfo.values()) { 0058 std::tie(category, value) = item; 0059 dbSearchInfo.addAnd(category, value); 0060 } 0061 return dbSearchInfo; 0062 } 0063 0064 void RemoteInterface::pixmapLoaded(ImageManager::ImageRequest *request, const QImage &image) 0065 { 0066 m_connection->sendCommand(ThumbnailResult(m_imageNameStore[request->databaseFileName()], QString(), 0067 image, static_cast<RemoteImageRequest *>(request)->type())); 0068 } 0069 0070 bool RemoteInterface::requestStillNeeded(const DB::FileName &fileName) 0071 { 0072 return m_activeReuqest.contains(fileName); 0073 } 0074 0075 void RemoteInterface::listen(QHostAddress address) 0076 { 0077 m_connection->listen(address); 0078 Q_EMIT listening(); 0079 } 0080 0081 void RemoteInterface::stopListening() 0082 { 0083 m_connection->stopListening(); 0084 } 0085 0086 void RemoteInterface::connectTo(const QHostAddress &address) 0087 { 0088 m_connection->connectToTcpServer(address); 0089 } 0090 0091 void RemoteInterface::handleCommand(const RemoteCommand &command) 0092 { 0093 if (command.commandType() == CommandType::SearchRequest) { 0094 const SearchRequest &searchCommand = static_cast<const SearchRequest &>(command); 0095 if (searchCommand.type == SearchType::Categories) 0096 sendCategoryNames(searchCommand); 0097 else if (searchCommand.type == SearchType::CategoryItems) 0098 sendCategoryValues(searchCommand); 0099 else 0100 sendImageSearchResult(searchCommand.searchInfo); 0101 } else if (command.commandType() == CommandType::ThumbnailRequest) 0102 requestThumbnail(static_cast<const ThumbnailRequest &>(command)); 0103 else if (command.commandType() == CommandType::ThumbnailCancelRequest) 0104 cancelRequest(static_cast<const ThumbnailCancelRequest &>(command)); 0105 else if (command.commandType() == CommandType::ImageDetailsRequest) 0106 sendImageDetails(static_cast<const ImageDetailsRequest &>(command)); 0107 else if (command.commandType() == CommandType::StaticImageRequest) 0108 sendHomePageImages(static_cast<const StaticImageRequest &>(command)); 0109 else if (command.commandType() == CommandType::ToggleTokenRequest) 0110 setToken(static_cast<const ToggleTokenRequest &>(command)); 0111 } 0112 0113 void RemoteInterface::sendCategoryNames(const SearchRequest &search) 0114 { 0115 const DB::ImageSearchInfo dbSearchInfo = convert(search.searchInfo); 0116 0117 CategoryListResult command; 0118 for (const DB::CategoryPtr &category : DB::ImageDB::instance()->categoryCollection()->categories()) { 0119 if (category->type() == DB::Category::MediaTypeCategory) 0120 continue; 0121 QMap<QString, DB::CountWithRange> images = DB::ImageDB::instance()->classify(dbSearchInfo, category->name(), DB::Image); 0122 0123 QMap<QString, DB::CountWithRange> videos = DB::ImageDB::instance()->classify(dbSearchInfo, category->name(), DB::Video); 0124 const bool enabled = (images.count() /*+ videos.count()*/ > 1); 0125 CategoryViewType type = (category->viewType() == DB::Category::IconView || category->viewType() == DB::Category::ThumbedIconView) 0126 ? Types::CategoryIconView 0127 : Types::CategoryListView; 0128 0129 const QImage icon = category->icon(search.size, enabled ? KIconLoader::DefaultState : KIconLoader::DisabledState).toImage(); 0130 command.categories.append({ category->name(), icon, enabled, type }); 0131 } 0132 m_connection->sendCommand(command); 0133 } 0134 0135 void RemoteInterface::sendCategoryValues(const SearchRequest &search) 0136 { 0137 const DB::ImageSearchInfo dbSearchInfo = convert(search.searchInfo); 0138 const QString categoryName = search.searchInfo.currentCategory(); 0139 0140 const DB::CategoryPtr category = DB::ImageDB::instance()->categoryCollection()->categoryForName(search.searchInfo.currentCategory()); 0141 0142 Browser::FlatCategoryModel model(category, dbSearchInfo); 0143 0144 if (category->viewType() == DB::Category::IconView || category->viewType() == DB::Category::ThumbedIconView) { 0145 QList<int> result; 0146 std::transform(model.m_items.begin(), model.m_items.end(), std::back_inserter(result), 0147 [this, categoryName](const QString itemName) { 0148 return m_imageNameStore.idForCategory(categoryName, itemName); 0149 }); 0150 m_connection->sendCommand(SearchResult(SearchType::CategoryItems, result)); 0151 } else { 0152 m_connection->sendCommand(CategoryItemsResult(model.m_items)); 0153 } 0154 } 0155 0156 void RemoteInterface::sendImageSearchResult(const SearchInfo &search) 0157 { 0158 const DB::FileNameList files = DB::ImageDB::instance()->search(convert(search), true /* Require on disk */).files(); 0159 DB::FileNameList stacksRemoved; 0160 QList<int> result; 0161 0162 std::remove_copy_if(files.begin(), files.end(), std::back_inserter(stacksRemoved), 0163 [](const DB::FileName &file) { 0164 // Only include unstacked images, and the top of stacked images. 0165 // And also exclude videos 0166 return DB::ImageDB::instance()->info(file)->stackOrder() > 1 || DB::ImageDB::instance()->info(file)->isVideo(); 0167 }); 0168 0169 std::transform(stacksRemoved.begin(), stacksRemoved.end(), std::back_inserter(result), 0170 [this](const DB::FileName &fileName) { 0171 return m_imageNameStore[fileName]; 0172 }); 0173 0174 m_connection->sendCommand(SearchResult(SearchType::Images, result)); 0175 } 0176 0177 void RemoteInterface::requestThumbnail(const ThumbnailRequest &command) 0178 { 0179 if (command.type == ViewType::CategoryItems) { 0180 auto tuple = m_imageNameStore.categoryForId(command.imageId); 0181 QString categoryName = tuple.first; 0182 QString itemName = tuple.second; 0183 0184 const DB::CategoryPtr category = DB::ImageDB::instance()->categoryCollection()->categoryForName(categoryName); 0185 QImage image = category->categoryImage(categoryName, itemName, command.size.width(), command.size.height()).toImage(); 0186 m_connection->sendCommand(ThumbnailResult(command.imageId, itemName, image, ViewType::CategoryItems)); 0187 } else { 0188 const DB::FileName fileName = m_imageNameStore[command.imageId]; 0189 const DB::ImageInfoPtr info = DB::ImageDB::instance()->info(fileName); 0190 const int angle = info->angle(); 0191 0192 m_activeReuqest.insert(fileName); 0193 0194 QSize size = command.size; 0195 if (!size.isValid()) { 0196 // Request for full screen image. 0197 size = info->size(); 0198 } 0199 RemoteImageRequest *request 0200 = new RemoteImageRequest(fileName, size, angle, command.type, this); 0201 0202 ImageManager::AsyncLoader::instance()->load(request); 0203 } 0204 } 0205 0206 void RemoteInterface::cancelRequest(const ThumbnailCancelRequest &command) 0207 { 0208 m_activeReuqest.remove(m_imageNameStore[command.imageId]); 0209 } 0210 0211 void RemoteInterface::sendImageDetails(const ImageDetailsRequest &command) 0212 { 0213 const DB::FileName fileName = m_imageNameStore[command.imageId]; 0214 const DB::ImageInfoPtr info = DB::ImageDB::instance()->info(fileName); 0215 ImageDetailsResult result; 0216 result.fileName = fileName.relative(); 0217 result.date = info->date().toString(); 0218 result.description = info->description(); 0219 result.categories.clear(); 0220 for (const QString &categoryName : info->availableCategories()) { 0221 DB::CategoryPtr category = DB::ImageDB::instance()->categoryCollection()->categoryForName(categoryName); 0222 CategoryItemDetailsList list; 0223 for (const QString &item : info->itemsOfCategory(categoryName)) { 0224 const QString age = Utilities::formatAge(category, item, info); 0225 list.append(CategoryItemDetails(item, age)); 0226 } 0227 result.categories[categoryName] = list; 0228 } 0229 0230 m_connection->sendCommand(result); 0231 } 0232 0233 void RemoteInterface::sendHomePageImages(const StaticImageRequest &command) 0234 { 0235 const int size = command.size; 0236 0237 QPixmap homeIcon = KIconLoader::global()->loadIcon(QString::fromUtf8("go-home"), KIconLoader::Desktop, size); 0238 QPixmap kphotoalbumIcon = KIconLoader::global()->loadIcon(QString::fromUtf8("kphotoalbum"), KIconLoader::Desktop, size); 0239 QPixmap discoverIcon = KIconLoader::global()->loadIcon(QString::fromUtf8("edit-find"), KIconLoader::Desktop, size); 0240 0241 m_connection->sendCommand(StaticImageResult(homeIcon.toImage(), kphotoalbumIcon.toImage(), discoverIcon.toImage())); 0242 } 0243 0244 void RemoteInterface::setToken(const ToggleTokenRequest &command) 0245 { 0246 const DB::FileName fileName = m_imageNameStore[command.imageId]; 0247 DB::ImageInfoPtr info = DB::ImageDB::instance()->info(fileName); 0248 DB::CategoryPtr tokensCategory = DB::ImageDB::instance()->categoryCollection()->categoryForSpecial(DB::Category::TokensCategory); 0249 if (command.state == ToggleTokenRequest::On) 0250 info->addCategoryInfo(tokensCategory->name(), command.token); 0251 else 0252 info->removeCategoryInfo(tokensCategory->name(), command.token); 0253 MainWindow::DirtyIndicator::markDirty(); 0254 } 0255 0256 #include "moc_RemoteInterface.cpp"