File: ArticleTablePlugin.m

package info (click to toggle)
grr.app 1.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,024 kB
  • sloc: objc: 4,019; sh: 54; makefile: 23
file content (266 lines) | stat: -rw-r--r-- 8,223 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/*
   Grr RSS Reader
   
   Copyright (C) 2006, 2007 Guenther Noack <guenther@unix-ag.uni-kl.de>
   Copyright (C) 2009-2012  GNUstep Application Team
                            Riccardo Mottola

   This application is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation; either
   version 3 of the License, or (at your option) any later version.
 
   This application is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.
 
   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. 
*/

#import "ArticleTablePlugin.h"

#import "Article.h"
#import "GNRatingCell.h"

#ifdef __APPLE__
#import "GNUstep.h"
#endif

int compareArticleHeadlines( id articleA, id articleB, void* context ) {
    id<Article> a = (id<Article>) articleA;
    id<Article> b = (id<Article>) articleB;
    
    return [[a headline] caseInsensitiveCompare: [b headline]];
}

int compareArticleDates( id articleA, id articleB, void* context ) {
    id<Article> a = (id<Article>) articleA;
    id<Article> b = (id<Article>) articleB;
    
    return [[a date] compare: [b date]];
}

int compareArticleRatings( id articleA, id articleB, void* context) {
    id<Article> a = (id<Article>) articleA;
    id<Article> b = (id<Article>) articleB;
    
    int ratingA = [a rating];
    int ratingB = [b rating];
    
    if (ratingA == ratingB) {
        return NSOrderedSame;
    } else if (ratingA > ratingB) {
        return NSOrderedAscending;
    } else {
        return NSOrderedDescending;
    }
}

@implementation ArticleTablePlugin

-(id) init
{
    [super init];
    [_view retain];
    return self;
}

-(void)awakeFromNib
{
  GNRatingCell *ratingCell;
  
  ASSIGN(table, [(NSScrollView*)_view documentView]);
  ASSIGN(headlineCol, [table tableColumnWithIdentifier: @"headline"]);
  ASSIGN(dateCol, [table tableColumnWithIdentifier: @"date"]);
  ASSIGN(ratingCol, [table tableColumnWithIdentifier: @"rating"]);
  
  ratingCell = [[GNRatingCell alloc] init]; 
  [ratingCol setDataCell:ratingCell];
  
  // Register for change notifications
  [[NSNotificationCenter defaultCenter] addObserver: self
                                           selector: @selector(articleChanged:)
                                               name: RSSArticleChangedNotification
                                             object: nil];

    [table setAutoresizesAllColumnsToFit: YES];

    // Ensure table is autosaved
    [table setAutosaveName: @"Article Table"];
    [table setAutosaveTableColumns: YES];
}

-(void) setNewArrayWithoutNotification: (NSArray*) newArray
{
    NSMutableIndexSet* indexSet;
    NSUInteger i;

    if ([newArray isEqual: articles]) {
        return;  // nothing changed
    }
    
    // Calculates the indexes of the currently selected articles
    // in the new table (if they are present)
    indexSet = [NSMutableIndexSet new];
    
    for (i=0; i<[articles count]; i++) { // for all row numbers in table
        if ([table isRowSelected: i]) {
            id article = [articles objectAtIndex: i];
            NSUInteger newIndex = [newArray indexOfObject: article];
            if (newIndex != NSNotFound) {
                [indexSet addIndex: newIndex];
            }
        }
    }
    
    ASSIGN(articles, newArray);
    
    // Important: reload *before* selecting, or the selection will be not appliable!
    [table reloadData];
    [table selectRowIndexes: indexSet byExtendingSelection: NO];
}

// --------------- MVC Model Change Listening --------------

-(void) articleChanged: (NSNotification*) aNotification
{
    // If we currently display the feed that changed, reload the data
    if ([articles containsObject: [aNotification object]]) {
        [table reloadData];
    }
}

// -------------- Component connections -------------------

-(NSSet*) objectsForPipeType: (id<PipeType>)aPipeType;
{
    NSAssert2(
        aPipeType == [PipeType articleType],
        @"%@ component does not support %@ output",
        self, aPipeType
    );
    
    if (articleSelection == nil) {
        int i;
        articleSelection = [[NSMutableSet alloc] init];
        for (i=0; i<[articles count]; i++) {
            if ([table isRowSelected: i]) {
                [articleSelection addObject: [articles objectAtIndex: i]];
            }
        }
    }
    
    return articleSelection;
}

-(void)componentDidUpdateSet: (NSNotification*) aNotification
{
    // Update articles
    ASSIGN(articles, [[[aNotification object] objectsForPipeType: [PipeType articleType]] allObjects]);
    
    // Reload table contents
    [table reloadData];
    
    [table deselectAll: self];
    
    // Notify listeners of change
    // Done automatically because of the above deselectAll: call and the notifyChanges in the
    // selection changed delegate method
    // [self notifyChanges];
}


// ---------------- NSTableView data source ----------------------

- (int) numberOfRowsInTableView: (NSTableView *)aTableView
{
    return [articles count];
}

- (id)           tableView: (NSTableView *)aTableView
 objectValueForTableColumn: (NSTableColumn *)aTableColumn
                       row: (int)rowIndex;
{
    id<Article> article = [articles objectAtIndex: rowIndex];
    
    if (aTableColumn == headlineCol) {
        return [article headline];
    } else if (aTableColumn == dateCol) {
        // FIXME: Make the date format configurable!
        return [[article date] descriptionWithCalendarFormat: @"%Y-%m-%d"
                               timeZone: nil
                               locale: nil];
    } else {
        NSAssert1(aTableColumn == ratingCol, @"Unknown table column \"%@\"", aTableColumn);
        return [NSNumber numberWithInt: [article rating]];
    }
}

// ------------------- NSTableView delegate ------------------------

- (void) tableViewSelectionDidChange: (NSNotification*) notif
{
    // clear article selection set
    ASSIGN(articleSelection, nil);
    
    [self notifyChanges];
}

-(void) tableView: (NSTableView*) aTableView
    willDisplayCell: (id)aCell
    forTableColumn: (NSTableColumn*) aTableColumn
    row: (int)rowIndex
{
    NSCell* cell = aCell;
    id<Article> article = [articles objectAtIndex: rowIndex];
    
    if ([article isRead]) {
        [cell setFont: [NSFont systemFontOfSize: [NSFont systemFontSize]]];
    } else {
        [cell setFont: [NSFont boldSystemFontOfSize: [NSFont systemFontSize]]];
    }
}

-(void) tableView: (NSTableView*) aTableView
  mouseDownInHeaderOfTableColumn: (NSTableColumn*) aTableColumn
{
    NSArray* newArray = nil;
    if (aTableColumn == headlineCol) {
        newArray = [articles sortedArrayUsingFunction: compareArticleHeadlines context: nil];
    } else if (aTableColumn == dateCol) {
        newArray = [articles sortedArrayUsingFunction: compareArticleDates context: nil];        
    } else if (aTableColumn == ratingCol) {
        newArray = [articles sortedArrayUsingFunction: compareArticleRatings context: nil];
    } else {
        [NSException raise: @"BadColumnException"
                    format: @"Unknown column %@", [aTableColumn identifier]];
    }
    
    [self setNewArrayWithoutNotification: newArray];
    [self notifyChanges];
}

-(void) tableView: (NSTableView*) aTableView
   setObjectValue: (id) anObj
   forTableColumn: (NSTableColumn*) aTableColumn
              row: (int) rowIndex
{
    if (aTableColumn == ratingCol) {
	id article;

        /* We can't keep that as an assertion now, as it can easily fail when
         * the broken GNUstep NSTableView lets you edit the string value for the cell.
         */
        if ([anObj isKindOfClass: [NSNumber class]] == NO) {
            NSLog(@"Warning: %@ is not a number value.", anObj);
        }
        
        article = [articles objectAtIndex: rowIndex];
        [article setRating: [anObj intValue]];
    }
}

@end