798e7f5eccd01044c78a2468470aa38f0e081d37
[xonotic/netradiant.git] / radiant / mru.cpp
1 /*
2    Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "mru.h"
23
24 #include <string.h>
25 #include <stdio.h>
26 #include <gtk/gtk.h>
27
28 #include "os/file.h"
29 #include "generic/callback.h"
30 #include "stream/stringstream.h"
31 #include "convert.h"
32
33 #include "gtkutil/menu.h"
34 #include "map.h"
35 #include "qe3.h"
36
37 const int MRU_MAX = 4;
38 namespace {
39         GtkMenuItem *MRU_items[MRU_MAX];
40         std::size_t MRU_used;
41         typedef CopiedString MRU_filename_t;
42         MRU_filename_t MRU_filenames[MRU_MAX];
43         typedef const char* MRU_key_t;
44         MRU_key_t MRU_keys[MRU_MAX] = { "File0", "File1", "File2", "File3" };
45 }
46
47 inline const char* MRU_GetText( std::size_t index ){
48         return MRU_filenames[index].c_str();
49 }
50
51 class EscapedMnemonic
52 {
53 private:
54         StringBuffer m_buffer;
55
56 public:
57         EscapedMnemonic( std::size_t capacity ) : m_buffer( capacity ){
58                 m_buffer.push_back( '_' );
59         }
60         const char* c_str() const {
61                 return m_buffer.c_str();
62         }
63         void push_back( char c ){ // not escaped
64                 m_buffer.push_back( c );
65         }
66         std::size_t write( const char* buffer, std::size_t length ){
67                 for ( const char* end = buffer + length; buffer != end; ++buffer )
68                 {
69                         if ( *buffer == '_' ) {
70                                 m_buffer.push_back( '_' );
71                         }
72
73                         m_buffer.push_back( *buffer );
74                 }
75                 return length;
76         }
77 };
78
79 template<typename T>
80 inline EscapedMnemonic& operator<<( EscapedMnemonic& ostream, const T& t ){
81         return ostream_write( ostream, t );
82 }
83
84
85 void MRU_updateWidget( std::size_t index, const char *filename ){
86         EscapedMnemonic mnemonic( 64 );
87         mnemonic << Unsigned( index + 1 ) << "- " << filename;
88         gtk_label_set_text_with_mnemonic( GTK_LABEL( gtk_bin_get_child( GTK_BIN( MRU_items[index] ) ) ), mnemonic.c_str() );
89 }
90
91 void MRU_SetText( std::size_t index, const char *filename ){
92         MRU_filenames[index] = filename;
93         MRU_updateWidget( index, filename );
94 }
95
96 void MRU_AddFile( const char *str ){
97         std::size_t i;
98         const char* text;
99
100         // check if file is already in our list
101         for ( i = 0; i < MRU_used; i++ )
102         {
103                 text = MRU_GetText( i );
104
105                 if ( strcmp( text, str ) == 0 ) {
106                         // reorder menu
107                         for (; i > 0; i-- )
108                                 MRU_SetText( i, MRU_GetText( i - 1 ) );
109
110                         MRU_SetText( 0, str );
111
112                         return;
113                 }
114         }
115
116         if ( MRU_used < MRU_MAX ) {
117                 MRU_used++;
118         }
119
120         // move items down
121         for ( i = MRU_used - 1; i > 0; i-- )
122                 MRU_SetText( i, MRU_GetText( i - 1 ) );
123
124         MRU_SetText( 0, str );
125         gtk_widget_set_sensitive( ui::MenuItem::from(MRU_items[0]) , TRUE );
126         ui::MenuItem::from(MRU_items[MRU_used - 1] ).show();
127 }
128
129 void MRU_Init(){
130         if ( MRU_used > MRU_MAX ) {
131                 MRU_used = MRU_MAX;
132         }
133 }
134
135 void MRU_AddWidget( ui::MenuItem widget, std::size_t pos ){
136         if ( pos < MRU_MAX ) {
137                 MRU_items[pos] = widget;
138                 if ( pos < MRU_used ) {
139                         MRU_updateWidget( pos, MRU_GetText( pos ) );
140                         gtk_widget_set_sensitive( ui::MenuItem::from(MRU_items[0]) , TRUE );
141                         ui::MenuItem::from(MRU_items[pos]).show();
142                 }
143         }
144 }
145
146 void MRU_Activate( std::size_t index ){
147         char text[1024];
148         strcpy( text, MRU_GetText( index ) );
149
150         if ( file_readable( text ) ) { //\todo Test 'map load succeeds' instead of 'file is readable'.
151                 MRU_AddFile( text );
152                 Map_RegionOff();
153                 Map_Free();
154                 Map_LoadFile( text );
155         }
156         else
157         {
158                 MRU_used--;
159
160                 for ( std::size_t i = index; i < MRU_used; i++ )
161                         MRU_SetText( i, MRU_GetText( i + 1 ) );
162
163                 if ( MRU_used == 0 ) {
164                         auto label = ui::Label::from(gtk_bin_get_child(GTK_BIN(MRU_items[0] )) );
165                         label.text("Recent Files");
166                         gtk_widget_set_sensitive( ui::MenuItem::from(MRU_items[0]), FALSE );
167                 }
168                 else
169                 {
170                         ui::MenuItem::from(MRU_items[MRU_used]).hide();
171                 }
172         }
173 }
174
175
176 class LoadMRU
177 {
178 private:
179         std::size_t m_number;
180
181 public:
182         LoadMRU( std::size_t number )
183                 : m_number( number ){
184         }
185         void load(){
186                 if ( ConfirmModified( "Open Map" ) ) {
187                         MRU_Activate( m_number - 1 );
188                 }
189         }
190 };
191
192 typedef MemberCaller<LoadMRU, void(), &LoadMRU::load> LoadMRUCaller;
193
194 LoadMRU g_load_mru1( 1 );
195 LoadMRU g_load_mru2( 2 );
196 LoadMRU g_load_mru3( 3 );
197 LoadMRU g_load_mru4( 4 );
198
199 void MRU_constructMenu( ui::Menu menu ){
200         {
201                 auto item = create_menu_item_with_mnemonic( menu, "_1", LoadMRUCaller( g_load_mru1 ) );
202                 gtk_widget_set_sensitive( item , FALSE );
203                 MRU_AddWidget( item, 0 );
204         }
205         {
206                 auto item = create_menu_item_with_mnemonic( menu, "_2", LoadMRUCaller( g_load_mru2 ) );
207                 item.hide();
208                 MRU_AddWidget( item, 1 );
209         }
210         {
211                 auto item = create_menu_item_with_mnemonic( menu, "_3", LoadMRUCaller( g_load_mru3 ) );
212                 item.hide();
213                 MRU_AddWidget( item, 2 );
214         }
215         {
216                 auto item = create_menu_item_with_mnemonic( menu, "_4", LoadMRUCaller( g_load_mru4 ) );
217                 item.hide();
218                 MRU_AddWidget( item, 3 );
219         }
220 }
221
222 #include "preferencesystem.h"
223 #include "stringio.h"
224
225 void MRU_Construct(){
226         GlobalPreferenceSystem().registerPreference( "Count", make_property_string( MRU_used ) );
227
228         for ( std::size_t i = 0; i != MRU_MAX; ++i )
229         {
230                 GlobalPreferenceSystem().registerPreference( MRU_keys[i], make_property_string( MRU_filenames[i] ) );
231         }
232
233         MRU_Init();
234 }
235 void MRU_Destroy(){
236 }