4
4
#include < QJsonDocument>
5
5
#include < QJsonArray>
6
6
7
+ const int MacroBrowserUI::FileRole = Qt::UserRole + 0 ;
8
+ const int MacroBrowserUI::UrlRole = Qt::UserRole + 1 ;
9
+ const int MacroBrowserUI::PathRole = Qt::UserRole + 2 ;
10
+ const int MacroBrowserUI::PopulatedRole = Qt::UserRole + 3 ;
11
+ Q_DECLARE_METATYPE (QTreeWidgetItem *)
7
12
8
13
MacroBrowserUI::MacroBrowserUI(QWidget *parent):QDialog (parent)
9
14
{
10
- tableWidget=new QTableWidget (4 ,1 );
11
- tableWidget->setHorizontalHeaderLabels (QStringList ()<<tr (" Macro name" ));
12
- tableWidget->setEditTriggers (QAbstractItemView::NoEditTriggers);
13
- tableWidget->horizontalHeader ()->setStretchLastSection (true );
14
- connect (tableWidget,SIGNAL (itemClicked (QTableWidgetItem*)),SLOT (itemClicked (QTableWidgetItem*)));
15
+ treeWidget=new QTreeWidget (parent);
16
+ treeWidget->setHeaderHidden (true );
15
17
auto *lblName=new QLabel (tr (" Name" ));
16
18
lblName->setAlignment (Qt::AlignRight);
17
19
auto *lblDescription=new QLabel (tr (" Description" ));
@@ -24,16 +26,14 @@ MacroBrowserUI::MacroBrowserUI(QWidget *parent):QDialog (parent)
24
26
gridLay->setColumnStretch (0 ,1 );
25
27
gridLay->setColumnStretch (1 ,0 );
26
28
gridLay->setColumnStretch (2 ,1 );
27
- gridLay->addWidget (tableWidget ,1 ,0 ,5 ,1 );
29
+ gridLay->addWidget (treeWidget ,1 ,0 ,5 ,1 );
28
30
gridLay->addWidget (lblName,1 ,1 );
29
31
gridLay->addWidget (lblDescription,2 ,1 );
30
32
gridLay->addWidget (leName,1 ,2 );
31
- gridLay->addWidget (teDescription,2 ,2 );
33
+ gridLay->addWidget (teDescription,2 ,2 , 4 , 1 );
32
34
33
35
buttonBox=new QDialogButtonBox ();
34
36
buttonBox->setStandardButtons (QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
35
- connect (buttonBox, &QDialogButtonBox::accepted, this , &QDialog::accept);
36
- connect (buttonBox, &QDialogButtonBox::rejected, this , &QDialog::reject);
37
37
auto *layout=new QVBoxLayout ();
38
38
layout->addLayout (gridLay);
39
39
layout->addWidget (buttonBox);
@@ -42,7 +42,13 @@ MacroBrowserUI::MacroBrowserUI(QWidget *parent):QDialog (parent)
42
42
config=dynamic_cast <ConfigManager *>(ConfigManagerInterface::getInstance ());
43
43
networkManager = new QNetworkAccessManager ();
44
44
45
- requestMacroList (" " );
45
+ connect (treeWidget, SIGNAL (currentItemChanged (QTreeWidgetItem*,QTreeWidgetItem*)),SLOT (slotCurrentItemChanged (QTreeWidgetItem*)));
46
+ connect (treeWidget, SIGNAL (itemClicked (QTreeWidgetItem*,int )),SLOT (slotItemClicked (QTreeWidgetItem*)));
47
+ connect (treeWidget, SIGNAL (itemActivated (QTreeWidgetItem*,int )),SLOT (slotItemClicked (QTreeWidgetItem*)));
48
+ connect (treeWidget, SIGNAL (itemExpanded (QTreeWidgetItem*)),SLOT (slotItemExpanded (QTreeWidgetItem*)));
49
+ connect (buttonBox, &QDialogButtonBox::accepted, this , &QDialog::accept);
50
+ connect (buttonBox, &QDialogButtonBox::rejected, this , &QDialog::reject);
51
+ requestMacroList ();
46
52
}
47
53
48
54
MacroBrowserUI::~MacroBrowserUI ()
@@ -51,20 +57,16 @@ MacroBrowserUI::~MacroBrowserUI()
51
57
networkManager->deleteLater ();
52
58
networkManager=nullptr ;
53
59
}
54
- foreach (QList<QTableWidgetItem *>lst,itemCache){
55
- foreach (auto *item,lst){
56
- delete item;
57
- }
58
- }
60
+ treeWidget->~QTreeWidget ();
59
61
}
60
62
61
63
QList<Macro> MacroBrowserUI::getSelectedMacros ()
62
64
{
63
65
QList<Macro> lst;
64
- foreach (QList<QTableWidgetItem *>listOfItems,itemCache){
66
+ foreach (QList<QTreeWidgetItem *>listOfItems,itemCache){
65
67
foreach (auto *item,listOfItems){
66
- if (item->checkState ()==Qt::Checked){
67
- QString url=item->data (Qt::UserRole ).toString ();
68
+ if (item->checkState (0 )==Qt::Checked) {
69
+ QString url=item->data (0 ,UrlRole ).toString ();
68
70
QString macroJson=cache.value (url);
69
71
if (!macroJson.isEmpty ()){
70
72
Macro m;
@@ -78,25 +80,34 @@ QList<Macro> MacroBrowserUI::getSelectedMacros()
78
80
return lst;
79
81
}
80
82
81
- const QNetworkRequest::Attribute AttributeDirectURL = static_cast <QNetworkRequest::Attribute>(QNetworkRequest::User);
82
- const QNetworkRequest::Attribute AttributeURL = static_cast <QNetworkRequest::Attribute>(QNetworkRequest::User+1 );
83
+ const QNetworkRequest::Attribute mbAttributeItem = static_cast <QNetworkRequest::Attribute>(QNetworkRequest::User);
84
+ const QNetworkRequest::Attribute mbAttributeIsFile = static_cast <QNetworkRequest::Attribute>(QNetworkRequest::User+1 );
83
85
84
- void MacroBrowserUI::requestMacroList (const QString &path,const bool &directURL)
86
+ /* Note (1): The elements of the top level directory of the repository are added as top level items to the treeWidget. Thus they have no
87
+ * parent item (folder). Since folder items carry the path in the tree and the (repository) url as data, it is necessary to distinguish
88
+ * this situation where we use "" as path (think of the treeWidget as a folder).
89
+ */
90
+ void MacroBrowserUI::requestMacroList (QTreeWidgetItem *currentItem,const bool &isFile)
85
91
{
86
92
if (!networkManager){
87
- return ;
93
+ networkManager = new QNetworkAccessManager ();
94
+ if (!networkManager) return ;
88
95
}
89
- QString url=config->URLmacroRepository +path;
90
- // QString url="https://api.github.com/repos/sunderme/texstudio-macro/contents/"+path;
91
- if (directURL){
92
- url=path;
96
+ QString path=(currentItem ? currentItem->data (0 ,PathRole).toString () : " " ); // s. (1)
97
+ QString url;
98
+ if (isFile) {
99
+ // like https://raw.githubusercontent.com/texstudio-org/texstudio-macro/master/automatedTextmanipulation/autoLabel.txsMacro
100
+ url=(currentItem ? currentItem->data (0 ,UrlRole).toString () : " " ); // s. (1)
93
101
}else {
94
- currentPath=path;
102
+ path=(currentItem ? currentItem->data (0 ,PathRole).toString () : " " ); // s. (1)
103
+ // like https://api.github.com/repos/texstudio/texstudio-macro/contents/automatedTextmanipulation
104
+ url=config->URLmacroRepository +path;
95
105
}
106
+
96
107
QNetworkRequest request = QNetworkRequest (QUrl (url));
97
108
request.setRawHeader (" User-Agent" , " TeXstudio Macro Browser" );
98
- request.setAttribute (AttributeDirectURL,directURL );
99
- request.setAttribute (AttributeURL,url );
109
+ request.setAttribute (mbAttributeIsFile,isFile );
110
+ request.setAttribute (mbAttributeItem, QVariant::fromValue (currentItem) );
100
111
QNetworkReply *reply = networkManager->get (request);
101
112
connect (reply, SIGNAL (finished ()), SLOT (onRequestCompleted ()));
102
113
#if QT_VERSION_MAJOR<6
@@ -106,71 +117,20 @@ void MacroBrowserUI::requestMacroList(const QString &path,const bool &directURL)
106
117
#endif
107
118
}
108
119
109
- void MacroBrowserUI::itemClicked (QTableWidgetItem *item)
110
- {
111
- QString url=item->data (Qt::UserRole).toString ();
112
- if (url.isEmpty ()){
113
- // descend into folder
114
- leName->setText (" " );
115
- teDescription->setPlainText (" " );
116
- if (item->text ()==" .." ){
117
- int c=currentPath.lastIndexOf (' /' );
118
- url=currentPath.left (c);
119
- }else {
120
- url=currentPath+" /" +item->text ();
121
- }
122
- if (itemCache.contains (url)){
123
- // reuse cached
124
- currentPath=url;
125
- int i=0 ;
126
- for (int i=0 ;i<tableWidget->rowCount ();i++){
127
- tableWidget->takeItem (i,0 );
128
- }
129
- if (!url.isEmpty ()){
130
- auto *item=new QTableWidgetItem (QIcon::fromTheme (" file" )," .." );
131
- tableWidget->setRowCount (i+1 );
132
- tableWidget->setItem (i++,0 ,item);
133
- }
134
- foreach (QTableWidgetItem *item,itemCache.value (url)){
135
- tableWidget->setRowCount (i+1 );
136
- tableWidget->setItem (i++,0 ,item);
137
- }
138
- }else {
139
- requestMacroList (url);
140
- }
141
- }else {
142
- if (cache.contains (url)){
143
- // reuse cached
144
- QByteArray ba = cache.value (url).toUtf8 ();
145
- QJsonDocument jsonDoc=QJsonDocument::fromJson (ba);
146
- QJsonObject dd=jsonDoc.object ();
147
- leName->setText (dd[" name" ].toString ());
148
- QJsonArray array=dd[" description" ].toArray ();
149
- QVariantList vl=array.toVariantList ();
150
- QString text;
151
- foreach (auto v,vl){
152
- if (!text.isEmpty ()){
153
- text+=" \n " ;
154
- }
155
- text+=v.toString ();
156
- }
157
- teDescription->setPlainText (text);
158
- }
159
- else {
160
- requestMacroList (url,true );
161
- }
162
- }
163
- }
164
-
165
120
void MacroBrowserUI::onRequestError ()
166
121
{
167
122
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender ());
168
123
if (!reply) return ;
169
124
170
- QMessageBox::warning (this , tr (" Browse macro repository" ),
171
- tr (" Repository not found. Network error:%1" ).arg (" \n " +reply->errorString ()),
172
- QMessageBox::Ok,
173
- QMessageBox::Ok);
125
+ QString text=tr (" Repository not found. Network error:%1" );
126
+ QTreeWidgetItem *currentItem=reply->request ().attribute (mbAttributeItem).value <QTreeWidgetItem*>();
127
+ if (currentItem) {
128
+ currentItem->child (0 )->setText (0 ,text.arg (" \n " +reply->errorString ()));
129
+ }else {
130
+ QTreeWidgetItem *twi = new QTreeWidgetItem (QStringList () << text.arg (" \n " +reply->errorString ()));
131
+ treeWidget->addTopLevelItem (twi);
132
+ }
133
+
174
134
networkManager->deleteLater ();
175
135
networkManager=nullptr ;
176
136
}
@@ -180,10 +140,23 @@ void MacroBrowserUI::onRequestCompleted()
180
140
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender ());
181
141
if (!reply || reply->error () != QNetworkReply::NoError) return ;
182
142
143
+ QTreeWidgetItem *currentItem=reply->request ().attribute (mbAttributeItem).value <QTreeWidgetItem*>();
144
+ bool isFile=reply->request ().attribute (mbAttributeIsFile).toBool ();
145
+ QString path;
146
+ QString url;
147
+ if (isFile) {
148
+ url=(currentItem ? currentItem->data (0 ,UrlRole).toString () : " " ); // s. (1)
149
+ }else {
150
+ path=(currentItem ? currentItem->data (0 ,PathRole).toString () : " " ); // s. (1)
151
+ url=config->URLmacroRepository +path;
152
+ }
153
+
154
+ if (path!=" " && !currentItem) return ;
155
+
183
156
QByteArray ba = reply->readAll ();
184
157
185
- if (reply-> request (). attribute (AttributeDirectURL). toBool () ){
186
- // download requested
158
+ if (isFile ){
159
+ // file download requested
187
160
QJsonDocument jsonDoc=QJsonDocument::fromJson (ba);
188
161
QJsonObject dd=jsonDoc.object ();
189
162
leName->setText (dd[" name" ].toString ());
@@ -198,53 +171,89 @@ void MacroBrowserUI::onRequestCompleted()
198
171
}
199
172
teDescription->setPlainText (text);
200
173
// cache complete macro
201
- QString url=reply->request ().attribute (AttributeURL).toString ();
202
174
cache.insert (url,QString (ba));
203
175
}else {
204
- // folder overview requested
205
- // tableWidget->clearContents();
206
- for (int i=0 ;i<tableWidget->rowCount ();i++){
207
- tableWidget->takeItem (i,0 );
208
- }
176
+ // folder contents requested
209
177
QJsonDocument jsonDoc=QJsonDocument::fromJson (ba);
210
178
QJsonArray elements=jsonDoc.array ();
211
- int i=0 ;
212
- // add .. (up)
213
- if (!currentPath.isEmpty ()){
214
- auto *item=new QTableWidgetItem (QIcon::fromTheme (" file" )," .." );
215
- tableWidget->setRowCount (i+1 );
216
- tableWidget->setItem (i++,0 ,item);
179
+ if (currentItem) { // not needed when inserting top level items, s. (1)
180
+ currentItem->takeChildren ();
181
+ currentItem->setData (0 ,PopulatedRole,true );
217
182
}
218
- QList<QTableWidgetItem *> listOfItems;
183
+ QList<QTreeWidgetItem *> listOfItems;
219
184
foreach (auto element,elements){
220
185
QJsonObject dd=element.toObject ();
221
186
if (dd[" type" ].toString ()==" file" ){
222
187
QString name=dd[" name" ].toString ();
223
188
if (name.endsWith (" .txsMacro" )){
224
- auto *item=new QTableWidgetItem (QIcon::fromTheme (" file" ),name);
225
- item->setData (Qt::UserRole,dd[" download_url" ].toString ());
226
- item->setCheckState (Qt::Unchecked);
227
- tableWidget->setRowCount (i+1 );
228
- tableWidget->setItem (i++,0 ,item);
229
- if (i==1 ){
230
- requestMacroList (item->data (Qt::UserRole).toString (),true );
231
- }
232
- listOfItems<<item;
189
+ auto *twi=new QTreeWidgetItem (QStringList ()<<name);
190
+ twi->setData (0 ,FileRole,true );
191
+ twi->setData (0 ,UrlRole,dd[" download_url" ].toString ());
192
+ twi->setCheckState (0 ,Qt::Unchecked);
193
+ currentItem->addChild (twi);
194
+ listOfItems<<twi;
233
195
}
234
- }else {
235
- // folder
196
+ }else { // folder
236
197
QString name=dd[" name" ].toString ();
237
- auto *item=new QTableWidgetItem (QIcon::fromTheme (" folder" ),name);
238
- tableWidget->setRowCount (i+1 );
239
- tableWidget->setItem (i++,0 ,item);
198
+ auto *item=new QTreeWidgetItem (QStringList ()<<name);
199
+ QFont ft = item->font (0 );
200
+ ft.setBold (true );
201
+ item->setFont (0 ,ft);
202
+ item->setText (0 ,name);
203
+ QTreeWidgetItem *twi = new QTreeWidgetItem (QStringList () << tr (" <loading...>" ));
204
+ item->addChild (twi);
205
+ item->setData (0 ,FileRole,false );
206
+ item->setData (0 ,UrlRole, url);
207
+ item->setData (0 ,PathRole, path+name);
208
+ if (!currentItem) treeWidget->addTopLevelItem (item);
209
+ else item->addChild (item);
240
210
listOfItems<<item;
241
211
}
242
- // tableWidget->setRowCount(i);
243
212
}
244
- tableWidget->setCurrentCell (0 ,0 );
245
- itemCache.insert (currentPath,listOfItems);
213
+ itemCache.insert (path,listOfItems);
246
214
}
247
215
}
248
216
217
+ void MacroBrowserUI::slotCurrentItemChanged (QTreeWidgetItem *item)
218
+ {
219
+ bool isFile=item->data (0 ,FileRole).toBool ();
220
+ if (!isFile){
221
+ leName->setText (" " );
222
+ teDescription->setPlainText (" " );
223
+ }else {
224
+ QString url=item->data (0 ,UrlRole).toString ();
225
+ if (cache.contains (url)){
226
+ // reuse cached
227
+ QByteArray ba = cache.value (url).toUtf8 ();
228
+ QJsonDocument jsonDoc=QJsonDocument::fromJson (ba);
229
+ QJsonObject dd=jsonDoc.object ();
230
+ leName->setText (dd[" name" ].toString ());
231
+ QJsonArray array=dd[" description" ].toArray ();
232
+ QVariantList vl=array.toVariantList ();
233
+ QString text;
234
+ foreach (auto v,vl){
235
+ if (!text.isEmpty ()){
236
+ text+=" \n " ;
237
+ }
238
+ text+=v.toString ();
239
+ }
240
+ teDescription->setPlainText (text);
241
+ }
242
+ else {
243
+ requestMacroList (item,true );
244
+ }
245
+ }
246
+ }
249
247
248
+ void MacroBrowserUI::slotItemClicked (QTreeWidgetItem *item)
249
+ {
250
+ bool isFile=item->data (0 ,FileRole).toBool ();
251
+ if (!isFile) return ;
252
+ treeWidget->setCurrentItem (item); // this may trigger slotCurrentItemChanged
253
+ }
250
254
255
+ void MacroBrowserUI::slotItemExpanded (QTreeWidgetItem *item){
256
+ bool populated=item->data (0 ,PopulatedRole).toBool ();
257
+ if (populated) return ;
258
+ requestMacroList (item);
259
+ }
0 commit comments